SCO_HASH
an arbitrary precision big integer number
SCO_PUBLIC_KEY
+struct SCBigInt
+{
+ bool positive;
+ opaque magnitude<>;
func $binary_
func $vec_
func $map_
Type case SCO_BIGINT: construct a host object by first invoking the from_bytes_be
Error handling — favor traps over errors
commit one bit in the host value to denote a None value (analogous to Rust’s std::option)
--
You received this message because you are subscribed to the Google Groups "Stellar Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to stellar-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/stellar-dev/7772eac2-b2e1-40db-8105-c866052d24f3n%40googlegroups.com.
Hi Jay,The surface area of these host fns looks good for a first pass, assuming that we'll be adding host fns that interact with the ledger in a separate CAP.
SCO_HASHWhat's the use case for making hash a first-class type? I think we should omit this type and simply use SCO_BINARY. I think if there is any specialization of binary types we should probably maintain that in the SDK and not in the host type system.
an arbitrary precision big integer numberWill BigInt's be truly arbitrary precision, or just some very high precision? Is CAP-51 the place we will state the upper bound on the precision, or will that come later?
SCO_PUBLIC_KEYWhat's the use case for making public key a first-class type? Will account IDs appear as public keys or is that likely to be another type in the future?
SCBigInt lends itself to having multiple values for zero. In languages that use default values heavily the default value for zero will be negative. It would also be quite reasonable for someone to think they could set positive to true for zero. I think we should address this in the same way that the Go stdlib does, by changing 'positive' to 'negative' so that if the sign field is not set the value is default positive. (Other ecosystems like Java have preferred an enum that has a specific value sign value for zero, but I don't think that adds anything over the better behaving default.)
func $binary_The binary functions look like they cover a similar area as the vec functions, although they're a subset which is inconvenient. Could we expand the function set so that they match the vec type, and are just a specialized version of a vec?
Or maybe we should add U8 as a SCVal so that we can represent this inside the existing vec host fns?
I think we will need a way to iterate a map, either by getting the keys first which seems not efficient, or by getting an iterator value. i.e. SCO_ITERATOR. I'm not sure the best way to contain the key and value, there are probably a few ways: a vec and use vec functions, or we could have a map specific iterator with next_key and next_value fns that are only valid to call in succession.
The Conversion section discusses how to build SCO_BIGINT by referencing a Rust crate, we should probably specify this independent of any implementation though, and include test cases to validate in SDKs that implement it. All the existing SDKs are likely going to need to be expanded with this logic.
Error handling — favor traps over errorsI think this is the right call for type errors, since the SDK should provide type safety to the developer and therefore it is an SDK or host bug if a panic occurs because of a type error.For other types of errors I think this moves the problems described, and doesn't eliminate them.Developers writing contracts in languages such as Rust or Go, that have a heavy bias towards developers handling errors themselves, will now be writing panic heavy code that could panic at any moment. It will force developers to become intimately aware of when host fns could panic and the developer toolchain will not help them understand when they could be, or help them code around those edge cases.
I expect we will make the SDK do checks before calling a hostfn and return an error, but the result of this will be the same number of branches but extra host fn calls. For example, when getting a value from a map the SDK will call has() before calling get(), so that the SDK can return an Option<T>.
commit one bit in the host value to denote a None value (analogous to Rust’s std::option)I think this idea would be a very reasonable approach to take. We could commit the low bit in RawVal to signal that a value is present. The only types it would have an impact on is BitSet which would be reduced from 60 bites to 59, and the positive-i64/u63 type which would be reduced to u62. u62 would still be a substantial optimization and so I don't think we'd lose out here much. There could be an impact to Symbols, I'm not sure. The SDK would convert the RawVal that has value None (low bit = 0), or Some(T) (low bit = 1), to Option<T>.
SCO_HASHWhat's the use case for making hash a first-class type? I think we should omit this type and simply use SCO_BINARY. I think if there is any specialization of binary types we should probably maintain that in the SDK and not in the host type system.HASH is a fixed-size 32-byte binary which is fairly ubiquitous in the network / ecosystem. A general SCO_BINARY doesn't have a fixed size, so every codepath has a panic-due-to-wrong-size if we go that route. It's not impossible but I think it might make sense to keep HASH special.Also hashes are fairly strictly the output from hash functions and inputs to hash-checking functions, and are typically opaque / indivisible, and have security meaning. So I don't know that there's a lot of value in making it overlap with general binaries, and I can imagine errors being caught by differentiating the two.More generally, every type _could_ be a "general binary" -- integers, bignums, even maps and vectors -- since every value is "just bytes" at a low level; but if the operations that produce-and-consume them are all specialized, and users have strong expectations about the bytes that comprise them and what those bytes mean, it seems to me to make sense to specialize the object type code it's marked with. It's not like object type codes are scarce.
SCO_PUBLIC_KEY
What's the use case for making public key a first-class type? Will account IDs appear as public keys or is that likely to be another type in the future?
I think this is similar to the hash question: if we have functions that produce and consume PKs have very specific expectations of the bytes, and the bytes are otherwise opaque (nobody's reading or writing single bytes in the middle of a pubkey), it seems to me to make sense to differentiate it from SCO_BINARY. But I can also understand the counterargument that "if the host representation is just an un-transformed byte buffer, maybe just use BINARY".
an arbitrary precision big integer numberWill BigInt's be truly arbitrary precision, or just some very high precision? Is CAP-51 the place we will state the upper bound on the precision, or will that come later?Every type will have some size limit implied by the resource-metering aspects of this (and other) CAPs. Besides those costs I do not think there's any other size limit imposed by the software.
SCBigInt lends itself to having multiple values for zero. In languages that use default values heavily the default value for zero will be negative. It would also be quite reasonable for someone to think they could set positive to true for zero. I think we should address this in the same way that the Go stdlib does, by changing 'positive' to 'negative' so that if the sign field is not set the value is default positive. (Other ecosystems like Java have preferred an enum that has a specific value sign value for zero, but I don't think that adds anything over the better behaving default.)Fair point, this should probably change. We can also make it a little less error prone by having a 3-state enum ZERO, POSITIVE, and NEGATIVE with a magnitude body only in the POSITIVE and NEGATIVE states, and define-as-invalid a magnitude of all-zero bytes.
I think we will need a way to iterate a map, either by getting the keys first which seems not efficient, or by getting an iterator value. i.e. SCO_ITERATOR. I'm not sure the best way to contain the key and value, there are probably a few ways: a vec and use vec functions, or we could have a map specific iterator with next_key and next_value fns that are only valid to call in succession.I think the best way to deal with this is a lower-bound / upper-bound pair of functions you can pass a key into and get a key back from. An iterator doesn't have a meaningful XDR representation -- it'd break the bijection we're maintaining between host values and XDR values.
--
You received this message because you are subscribed to the Google Groups "Stellar Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to stellar-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/stellar-dev/73c5e3b9-c3a2-4469-894a-02e958ba871fn%40googlegroups.com.
Got it, this sounds fine then. Could we add host fns for converting from SCO_BINARY to SCO_HASH/PUBLIC_KEY and back? And could we add host fns for SCO_ACCOUNT_ID to SCO_PUBLIC_KEY and back? I think we need to be able to interoperate with these types via binary for people to flexibly use their APIs.
I get that resource-metering will impose limits, but the VM is not the only stakeholder that will parse txns, and we need to communicate to other stakeholders what a reasonable tx is. We typically do that with the XDR definition. Similar to how we imposed an upper limit on WASM code size in the XDR, I think we should specify some upper limit on all the unbounded variable-length arrays, especially for any type that can be used inside a transaction.
Is the idea that you pass in a key, and you get the next key in the map? That sounds fine, and that sounds like an iterator to me and would do the job.
I get that resource-metering will impose limits, but the VM is not the only stakeholder that will parse txns, and we need to communicate to other stakeholders what a reasonable tx is. We typically do that with the XDR definition. Similar to how we imposed an upper limit on WASM code size in the XDR, I think we should specify some upper limit on all the unbounded variable-length arrays, especially for any type that can be used inside a transaction.Well, I think we actually just _removed_ the WASM code size from the XDR, moving it to a CONFIG ledger entry so the network can negotiate it up or down without a protocol change.(And this is doubly true with the move to storing code under a plain BINARY in a CONTRACT_DATA ledger entry -- the size of a binary is presumably not related to the size of a wasm)I get your point that 3rd parties need to be able to evaluate limits, but at present I think the current plan is to tell people to consult the current CONFIG ledger entries, whose values are all TBD.
Returning to that earlier question of explicit error-status code / sentinel values (I know I keep saying no to them ubiquitously, but I do recognize they're useful sometimes), I've been thinking about the ambiguity issue and thinking there might be a way around it for functions we want to use status codes with. If we carved off a single bit of the existing status type -- call it the "hot" bit -- and required that any status SCVal converted to a host value as well as any status host value passed to a host function has to have the hot bit cleared, then we could say that the hot bit is reserved for a status that _is_ representative of a currently-returning host-function failure. If anyone wants to reify a status into a longer-lived value (eg. put it in a container, pass it into a host function for use elsewhere) they have to clear the hot bit, mark it as a "cold" status. Still lets users store statuses as first class values if they want, but requires their use is unambiguous (or less ambiguous). If you call a function and you get back a hot status, it means the function failed. If you get a cold status, it means the function succeeded and is returning a status someone gave it from somewhere else, somehow (eg. put in a data structure, returned from an inner call, etc.)WDYT? It is still possible to have _some_ ambiguity (cold status relayed from 1 callee deep vs. N callees deep, say) but not in the case where it's important, trying to interpret the return from a call that actually failed.
> I thought we agreed otherwise in last week's protocol meeting, but I might be mistaken. I don't think it is practical for applications ingesting XDR to consult the value of CONFIG ledger entries that were set at each ledger. I understand there will be config ledger entries that define the currently configured boundaries, but we should define some upper bound that is more reasonable than XDR's default 4GB maximum. SCO_BINARY should have an upper bound too, otherwise there is no way for an ingester or parser of network transactions to be able to anticipate what valid data should be according to the XDR definition.
Oh, possibly! I don't recall, I just know (a) there are CONFIG LEs
planned and (b) there aren't currently XDR-imposed limits on BINARY. I
don't mind there being a smaller one. As a wise person once said, 64kb
ought to be enough for anyone!
--
You received this message because you are subscribed to the Google Groups "Stellar Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to stellar-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/stellar-dev/CANhY3x5xS8QGoJJVTO6kfje%3D%3DsrMn0jBSgav4D6SnNf7GUHW%2BQ%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/stellar-dev/CAC7ST6yVoQ_hg_N7SGUwffvvvfjfnpALHWecQepkKDjMM-E%2B4Q%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/stellar-dev/CAC7ST6yVoQ_hg_N7SGUwffvvvfjfnpALHWecQepkKDjMM-E%2B4Q%40mail.gmail.com.
- Why are Hash and PublicKey first-class objects?
@Leigh McCulloch You also mentioned (in a discord thread) that the guarantees (e.g. length) that these objects provide are only one-sided, only on the host side, not on the contracts. I don't think I fully understand this aspect. Would you mind elaborate on it a bit more (if this is still a concern)?
Also, you raised a good point of the possibility of including other Hash and PublicKey types (like ecdsa p256/secp256k1), should we change the current XDR representation to make it extensible?
- Conversion between SCO_BIGINT and host object should be implementation dependent
@Leigh McCulloch I agree this semantics should be generic instead of tied to a Rust implementation. Why does the SDK need to implement conversion between SCO types and the host types (I thought that is done only on the host side)? Maybe this is something obvious that I'm not getting at.
- Go stdlib’s signness issue
@Leigh McCulloch I'm not familiar with this issue, but I think this is a great catch! Do you happen to have a link to the Go stdlib or other resource where this is being discussed in depth, so that I can fully appreciate the nuances? I will go with Graydon’s suggestion of using enum but would like to understand the issue fully.
Regarding the issue of XDR size limit. I'm a little skeptical we should place a hard cap on the variable length arrays at the XDR level.For binary, as length corresponds to the number of bytes and we can have a reasonably good estimate of contract size limit so this might be plausible. However, ScVec is a heterogeneous structure which can be deeply nested, which makes the length cap kind of pointless. For BigInt, circling back to an earlier point by @Leigh McCulloch, there is no precision limit at implementation level (it is just an array of digits). So the actual precision limit will be bounded heavily by the operation. I'm just not sure what benefits the array length limits provide other than specifying an absolutely impossible to exceed limit where the actual limit is bounded by the application/gas limitation.