Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ABI: enums, structs and timestamps #50

Open
chriseth opened this issue Jan 8, 2016 · 14 comments

Comments

Projects
None yet
8 participants
@chriseth
Copy link
Contributor

commented Jan 8, 2016

This is a combination of several already existing issues about extensions to the ABI. It subsumes #21, #46 and #47.

Time Units

Add the following new elementary types to the ABI:
timestamp, timespan
Encoding: identical to int256 for both
Semantics:

  • timestamp: point in time as seconds since the unix epoch (Discussion: As we have int256 we could also make it milli or microseconds)
  • timespan: a span of time in seconds (or smaller unit as above)

Enums

At any point in an interface definition where an elementary type is allowed, the following is also allowed:
(A|), (A|B), (A|B|C), ... for A, B, C being from [_$a-zA-Z][_$a-zA-Z0-9]*

Such a type is called an "enum" and the identifiers A, B, C, ... are encoded as 0, 1, 3, ..., respectively. This means that languages and user interfaces should prefer names over numeric values, but the names are not visible in the binary encoding and the type is treated identical to uint256.

Structs

Structs allow several values to be grouped together. This is especially useful if arrays of structs are created. If you look closely, you notice that the set of return values or arguments is already a struct now and the notation will be consistent with this proposal.

Change to function signatures as needed for function identifiers

As function signatures omit parameter names and only specify their types, and struct types should be a generalisation of a list of parameters / return parameters, the following change is proposed:

At any point where a type is allowed, a list of types in the from (type1,type2,..) is permitted, to the extent where there may be only one type. Examples:

function f((uint256[],uint8,(string,bytes20),(string))[20])

The function takes an array of 20 objects, each of which is of the following type:

  • The first value is a dynamic array of uint256s,
  • the second is a uint8,
  • the third is an object that consists of a string and a bytes20
  • the fourth is an object that only holds a string

Note that this notation is (hopefully) bijective.

Encoding

Structs are actually already part of the encoding specification, hidden in this comment. The gist is that the way arrays are encoded does not rely on the fact that the encoding of every element of the array is the same, so we can encode structs in just the same way as we encode arrays. This means that the encoding of a struct with only fixed-size types is the concatenation of the encodings of its elements. If there are dynamic types, their dynamic part is added at the end and the offset to the dynamic part is calculated from the start of the struct. This means that the offset to the string data is computed in a different way for (uint256,string) and (uint256,(string)).

JSON-formatted abi:

inputs and outputs is an array of objects with properties name (optional), type, subtype (optional, new) and indexed (for events). type can be any of the already supported types ("uint256", "uint256[10][]", ...), but additional values are permitted:

  • "[]" or "[k]" for some integer k (or a sequence of those, i.e. [][10][]): If this is used, the object is of array type and the type of the elements of the arrays is specified under the property subtype.
  • an non-empty array of objects as described here - this models a tuple.

If the subtype property is used, its value cannot be "[]" or "[k]", but it can be an array of length 1 whose element models the type one deeper in the array hierarchy.

Example:

The signature function f(abc: uint, def: (a: uint, b: (c: string, d: uint16)[10]))) is translated into json-abi as follows:

{
  name: "f",
  inputs: [
    {name: "abc", type: "uint256"},
    {name: "def", type: [
      {name: "a", type: "uint256"},
      {name: "b", type: "[10]", subtype: [{name: "c", type: "string"}, {name: "d", type: "uint16"}]}
    ]}
  ]
}

@wanderer wanderer added the ABI label Jan 13, 2016

@androlo

This comment has been minimized.

Copy link

commented May 13, 2016

About timespan: Not very important, but I know duration is used a lot, boost time, Go and Java standard libraries, etc.

@axic

This comment has been minimized.

Copy link
Member

commented May 26, 2016

As per my comment on #47 I still think enum's should be represented as enum(A,B) (or enum(A|B)).

timestamp is signed since you count from the unix epoch, but why is timespan signed?

@chriseth

This comment has been minimized.

Copy link
Contributor Author

commented May 26, 2016

timestamp is problematic (as a keyword) because it would render block.timestamp invalid.

@chriseth

This comment has been minimized.

Copy link
Contributor Author

commented Jan 24, 2017

Note that recursive data structures are supported by the encoding, but not by the signature and are thus not allowed for now.

@axic axic referenced this issue Feb 9, 2017

Closed

Support returning structs #1601

0 of 2 tasks complete
@chriseth

This comment has been minimized.

Copy link
Contributor Author

commented Jun 29, 2017

@VoR0220 suggested that the types of json fields should be fixed, i.e. type should always be a string. This is probably something we should follow. Unfortunately, I do not remember exactly what he suggested, but I think the following would be the simplest solution to that problem:

`inputs` and` outputs` is an array of objects with properties
 `name` (optional), `type`, `subtype` (optional, new) and
 `indexed` (for events). `type` can be any of the already
 supported types ("uint256", "uint256[10][]", ...), but 
additional values are permitted:

`"[]"` or `"[k]"` for some integer `k`, or a sequence of 
those, i.e. `[][10][]`, including the empty string `""`: If this is
 used, the object is of array type and the type of the elements
 of the arrays is a tuple specified under the property `subtype`.
 If `type` is the empty string, it is just a tuple type.
@MicahZoltu

This comment has been minimized.

Copy link
Contributor

commented Aug 22, 2017

@axic timespan is signed because it has a direction and a magnitude encoded in it. A timespan of -1 would be one second backward in time.

timestamp now = block.timestamp
timestamp stored_timestamp = ...
timespan difference = now - stored_timestamp

In the above example, difference may be positive or negative depending on what the value of stored_timestamp is (whether it is greater than or less than block.timestamp).

@MicahZoltu

This comment has been minimized.

Copy link
Contributor

commented Aug 22, 2017

I would very much prefer it if timestamp and timespan were both in milliseconds and both signed.

It is unclear to me why block.timestamp decided to use uint256 seconds (though I think the unsigned part is actually a bug/annoyance in Solidity, rather than an issue with the EVM). The main reason I think both should be signed is because negative timespans are very useful, and a simple typechecker will complain if you try to do arithmatic with a mix of signed and unsigned.

@MicahZoltu

This comment has been minimized.

Copy link
Contributor

commented Aug 22, 2017

@Souptacular What, if anything, can be done to get this EIP moved forward? It doesn't seem to have much/any active discussion going on, but has been sitting for months.

The lack of structs as part of the ABI combined with 16 variable stack limit is really hurting Augur's port to Solidity from Serpent.

@willwarren89

This comment has been minimized.

Copy link

commented Aug 22, 2017

+1 on this. The contract ABI for 0x's Exchange.sol is a disaster without struct support combined with the 16 variable stack limit.

@chriseth

This comment has been minimized.

Copy link
Contributor Author

commented Aug 23, 2017

@MicahZoltu The struct part is being implemented, no need to push this EIP. timestamps and enums or on hold because of the problem mentioned above and the fact that adding support for enums is a breaking change in the ABI for Solidity.

@axic

This comment has been minimized.

Copy link
Member

commented Sep 13, 2017

It is unclear to me why block.timestamp decided to use uint256 seconds (though I think the unsigned part is actually a bug/annoyance in Solidity, rather than an issue with the EVM).

@MicahZoltu it maps to the TIMESTAMP opcode without any changes. It is just unsigned in EVM.

YP:

timestamp: A scalar value equal to the reasonable output of Unix’s time() at this block’s inception; formally Hs.

@MicahZoltu

This comment has been minimized.

Copy link
Contributor

commented Sep 18, 2017

So this is an issue with the EVM considering a timetstamp to be unsigned? Unix time is pretty universally considered to be signed since it centers on a point in time in the relatively near past.

@MicahZoltu

This comment has been minimized.

Copy link
Contributor

commented Sep 18, 2017

@chriseth Would it make sense to create an EIP PR for the struct part of this so that it can be tracked and eventually merged without having to wait on the other aspects of this EIP? In general I'm a fan of small targeted EIPs over monolithic bucket EIPs like this.

@chriseth

This comment has been minimized.

Copy link
Contributor Author

commented Sep 19, 2017

@MicahZoltu yeah, we should probably write that up. Here is the complete specification that is currently being implemented by Solidity: https://solidity.readthedocs.io/en/develop/abi-spec.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.