-
Notifications
You must be signed in to change notification settings - Fork 677
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
Clarity should permit local binding of global symbols #3182
Comments
The consensus from #3176 is that all Clarity keywords should be reserved, and unbindable (including ones that are not reserved today). The rationale is to ensure that code remains readable. Developer cleverness and flexibility can, in general, be sacrificed to ensure that the code is easier for other people to read (since the code can manage digital assets that have non-trivial real-world value). |
@jcnelson There is no such consensus in #3176. The readability benefits of reserving keywords are minor and dwarfed by the negative consequences, including that adding a new native Clarity function is a breaking change requiring complex workarounds like #3131 for the VM to handle concurrent versions of Clarity. Reserving keywords complicates code maintenance and sacrifices the security benefits of a simpler VM implementation for dubious gains in readability. |
@njordhov Respectfully, you seem to be the only one who wants this feature. None of the other folks who regularly contribute to this repo want this change, for the reasons outlined above. Even if making it possible for local binding of global symbols was a universally desired feature, doing so would not obviate #3131. This is because some of the new keywords introduced in Clarity2 either (1) access state that the VM currently cannot access (e.g. |
@jcnelson This is not a request for a "feature" but about correcting a severe flaw in the technical architecture. Adding new keywords to Clarity shouldn't cause a breaking version of the language. If we don't reserve keywords but allow rebinding, the names of new native functions won't conflict with symbols used in existing contracts, enabling legacy contracts to be evaluated the same as contracts calling the new functions. Do you have a concrete example of Clarity1 contract code that would no longer evaluate after introducing |
There is no breaking change to the language. All Clarity1 code will continue to be supported indefinitely, since all future versions of the blockchain must be able to process the existing chainstate the exact same way as it currently does. Moreover, with #3131, developers can choose which version of the VM will be used to evaluate their code (regardless of the versions of the other contracts that call into it). However, the blockchain will need to perform a coordinated breaking change, because the VM will suddenly support new keywords that would be valid in Clarity2, but are invalid today.
#3131 makes it so that even if a Clarity1 contract defined a function called EDIT: What happens now is that the VM tracks which version of Clarity to use when evaluating a particular function code body. All contracts today are Clarity1 contracts, and follow Clarity1 evaluation rules. This means that a Clarity1 contract that binds a reserved keyword in Clarity2 will continue to work, because in Clarity1 rules, the Clarity2 keywords are not recognized as keywords. Clarity2 contracts can call into Clarity1 contracts, and vice versa, but the function code body will be evaluated according to the function's contract's Clarity version. So, a Clarity2 contract can call a Clarity1 contract function called
|
It's actually the other way around: The Clarity VM will suddenly reserve keywords that are valid today.
With all due respect, there has been no consensus on this among Clarity contract developers, most of whom are unlikely to have any idea yet they will have to juggle multiple versions of the language.
The complexity added by #3131 would make the implementation harder to maintain and increase the chance of bugs. Complex code such as in PR #3192 are likely to have bugs, possibly severe ones. By avoiding or reducing unnecessary code complexity, we can improve maintainability and decrease the chance of bugs.
Well, say a currently deployed Clarity contract defines a trait with a function (define-trait burn-block-info
((get-burn-block-info? () principal))) Now, as Clarity2 introduces a built-in However, my point was that with reserved keywords, adding a new native Clarity function is a breaking change requiring complex workarounds like #3131 for the VM to handle concurrent versions of Clarity. Fortunately, the S-expression syntax of Clarity makes it unnecessary to reserve keywords, which opens for simplifying the VM instead of making the implementation more complex and Clarity development more complicated. |
The technical CAB voted overwhelmingly to implement the following in Stacks 2.1:
Furthermore, the ongoing SIP-015 vote is overwhelmingly "yes".
Making a variant of the Clarity VM which permits local binding of global symbols would also have this effect, so it's not clear that there's any gain here. In general, changing the semantics of the VM requires a breaking change and a new variant of the VM to be implemented, because the VM's evaluation of the transaction code is necessarily on the critical path for block validation. The unavoidable consequence of requiring that the developer's hand-written code be the code that gets interpreted upon transaction execution is that a change to the VM's behavior necessarily changes the calculation of the block's state root hash. Meaning, there exists a block such that if two different VM variants evaluate this block, they will calculate two different state root hashes (of which at most one can be valid). In addition, permitting contract code to change the meaning of language built-ins would create a means for nefarious actors to obfuscate their code, which we don't want. Anyway, I consider this issue to be closed, given that the CABs and STX voters have already affirmed the current system design. |
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Clarity should permit local binding of symbols having global definitions, making Clarity contracts more future-proof and avoiding the sacrifices and complications otherwise incurred.
Currently, contracts with local bindings of globally defined symbols can be deployed but will trigger a
NameAlreadyUsed
error during evaluation.Case in point, say we deploy a function
part
with a local binding of the symbolslice
:This contract runs fine as is, but fails after introducing a function named
slice
. As is, every new function added to the language is a breaking change, requiring special handling of existing contracts. Stacks 2.1 will introduce a nativeslice
function, and issue #3131 proposes a complicated workaround to avoid breaking existing contracts.But even having a breaking version of Clarity for each new native function won't alleviate all issues. For example, the contract above would fail deployment in Stacks 2.1 due to its native
slice
function. Moreover, developers (and software) cannot confidently reuse a function likepart
from an existing contract. Clarity code examples will have to be vetted for each new Clarity version to ensure there are no conflicts with newly introduced global symbols. And so on.Not allowing local binding of symbols with global definitions also complicates generating readable Clarity code. To avoid potential conflicts with global definitions, code generators may avoid using meaningful names for local bindings, instead opting for cryptic symbol names as in this obfuscated contract.
The fix is to remove the evaluation restriction on local bindings of globally defined symbols.
Related:
Issue #2696
Issue #3176
Issue #3131
The text was updated successfully, but these errors were encountered: