Smart Contract Lifecycle

104 views
Skip to first unread message

Siddharth Suresh

unread,
May 3, 2022, 5:11:17 PM5/3/22
to Stellar Developers
Hi everyone,

I published CAP-0047, which builds off of CAP-0046 and defines the semantics around creating and managing a contract on the Stellar network - https://github.com/stellar/stellar-protocol/blob/master/core/cap-0047.md.

Please let me know what you think.

Thanks,
Siddharth

Leigh McCulloch

unread,
May 4, 2022, 5:08:06 PM5/4/22
to Siddharth Suresh, Stellar Developers
Hi Siddharth,

The FunctionSignature has an SCValType alone for the type of the return value and arguments, but if the SCValType is an SCV_OBJECT then we also need to know the SCObjectType, otherwise the function signature will not state what type of object the argument is. This is one of the more inconvenient side-effects of our distinct value and object type lists: anywhere we wish to describe the full set of values/objects requires multiple values.

I note that the ContractBody can be updated on a ContractCodeEntry. How does an observer identify and refer to which ContractBody was used for a specific invocation? It might be important for auditing to be able to exactly reference a specific ContractBody that was used during an execution. Ideally that reference would show up somewhere in transaction results or meta so there is this clear trail of exactly what code executed for a given transaction. Is this address in another way that I'm not seeing?

Do we need ledger header controls for UpdateContractOp and RemoveContractOp? I can imagine situations where we also need to disable them.

Leigh

--
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/a0b925a7-7ea0-42bf-8a80-f2385697e320n%40googlegroups.com.

Siddharth Suresh

unread,
May 5, 2022, 8:02:07 PM5/5/22
to Leigh McCulloch, Stellar Developers

The FunctionSignature has an SCValType alone for the type of the return value and arguments, but if the SCValType is an SCV_OBJECT then we also need to know the SCObjectType, otherwise the function signature will not state what type of object the argument is. This is one of the more inconvenient side-effects of our distinct value and object type lists: anywhere we wish to describe the full set of values/objects requires multiple values.

Good catch Leigh. The CAP has been fixed.

I note that the ContractBody can be updated on a ContractCodeEntry. How does an observer identify and refer to which ContractBody was used for a specific invocation? It might be important for auditing to be able to exactly reference a specific ContractBody that was used during an execution. Ideally that reference would show up somewhere in transaction results or meta so there is this clear trail of exactly what code executed for a given transaction. Is this address in another way that I'm not seeing?

This is a good question. I'm not sure if we should add anything to the protocol for this. Core isn't going to keep around all versions of a contract, so even if we have some sort of identifier we can tie to an invocation, the user will need to pull the contract code from the specific ledger it was invoked. Maybe Horizon can keep around all contract versions (I'm not sure if this is feasible). We'd also need to think about how these identifiers are stored and how they propagate on cross contract invocations.

Do we need ledger header controls for UpdateContractOp and RemoveContractOp? I can imagine situations where we also need to disable them.

I updated the CAP to include flags for these operations. 

Thanks,
Siddharth

Nicolas Barry

unread,
May 6, 2022, 10:07:29 PM5/6/22
to Siddharth Suresh, Leigh McCulloch, Stellar Developers
Finally got some time to look into this.

Thank you for putting this together!

First, the easy stuff.

Parameters
I think we should stop using the LedgerHeader for storing settings: it doesn't scale well, and settings very rarely change.
Instead, I think we should take https://github.com/stellar/stellar-protocol/blob/master/core/cap-0044.md and generalize it: in that CAP it introduces a single, hard coded parameters, instead we'd introduce a new kind of ledger entries that are "owned" by the network, where the key is an enum of sorts that allows us to add many settings grouped into logical groups over time.

which brings me to: WASM size, this should be a network parameter. If we want to increase it in the future, having the limit in the XDR makes the upgrade more complicated (code would have to verify that the size is smaller if the protocol version is still the one with the previous value).


Semantics

Why are those classic operations?
Classic operations work with very different assumptions fee wise, and as the payloads are much larger in this context, we should avoid it.
Also, this would imply that you cannot manage contracts from smart contracts? I don't see any goals/requirements on that front.
Another way to do this would be to expose "host functions" to perform the same thing, and (possibly) a "default network contract" of sorts that is managed by the network (this could have other uses, see below) so that it's possible to invoke those host functions to spin up contracts on an empty ledger (bootstrap).

re: "immutable"
Is this needed? I don't see a rationale about this.
If it is needed, why is this not part of the manifest as to avoid the extra complexity?

manifest vs wasm
any reason to update them separately?
As the manifest contains the list of exports, it sounds dangerous to decouple their lifecycles.

re: global flags
I think we need to break down scenarios and have a rationale for this. I am not sure those flags meet requirements.
While we're ramping up smart contracts on Stellar, we probably need a (temporary) way to gradually open up the network to new authors. Like maybe only allow some accounts to publish (many solutions can be done outside of the protocol, assuming validators are all on board).
There are probably questions/concerns over the longer term as well that we should discuss. Like should validators have a way to deal with bad contracts in some way (this can be done outside of the protocol too, but much harder).



Nicolas


Nicolas Barry

unread,
May 6, 2022, 10:45:29 PM5/6/22
to Nicolas Barry, Siddharth Suresh, Leigh McCulloch, Stellar Developers
Actually re: “bootstrap” problem, should we instead consider allowing a wasm payload to be attached to a transaction and execute it without storing it in a ledger entry?
I think the only host functions we’d have to disable (if we can’t pick a good execution context) are storage related, but that would allow to invoke host functions and do some basic initialization atomically.


Nicolas


On May 6, 2022, at 19:07, Nicolas Barry <Nic...@stellar.org> wrote:



Siddharth Suresh

unread,
May 10, 2022, 8:04:32 PM5/10/22
to Nicolas Barry, Leigh McCulloch, Stellar Developers
Thanks for taking a look.

Parameters
I think we should stop using the LedgerHeader for storing settings: it doesn't scale well, and settings very rarely change.
Instead, I think we should take https://github.com/stellar/stellar-protocol/blob/master/core/cap-0044.md and generalize it: in that CAP it introduces a single, hard coded parameters, instead we'd introduce a new kind of ledger entries that are "owned" by the network, where the key is an enum of sorts that allows us to add many settings grouped into logical groups over time.

which brings me to: WASM size, this should be a network parameter. If we want to increase it in the future, having the limit in the XDR makes the upgrade more complicated (code would have to verify that the size is smaller if the protocol version is still the one with the previous value).
 
With this CAP, we have a total of 7 flags, and I don't think we would use that many more once we have smart contracts, so the odds of using all 32 are very low. Having said that, I agree we need something like CAP-0044 if we want to add more configurable values to the protocol. I think it would be a good idea to keep these emergency flags in the LedgerHeader, and include settings we expect to update like the WASM size into a configuration LedgerEntry.



Why are those classic operations?
Classic operations work with very different assumptions fee wise, and as the payloads are much larger in this context, we should avoid it.
Also, this would imply that you cannot manage contracts from smart contracts? I don't see any goals/requirements on that front.
Another way to do this would be to expose "host functions" to perform the same thing, and (possibly) a "default network contract" of sorts that is managed by the network (this could have other uses, see below) so that it's possible to invoke those host functions to spin up contracts on an empty ledger (bootstrap).

I was under the impression that the fee model for classic operations is going to change too, but it sounds like that is a change that I shouldn’t rely on in this CAP. I also thought a classic interface would be simple for users to understand. Using host functions to manage contracts, along with the ability to call contracts without storing them to “bootstrap” like you mentioned would work though. This would allow contracts to manage other contracts as well, if we decide to allow that. One point I’m not sure about yet is how you would submit this initial call to set up a contract. Is it a classic operation? If so, then we still have the issue with the fee model.


re: "immutable"
Is this needed? I don't see a rationale about this.
If it is needed, why is this not part of the manifest as to avoid the extra complexity?
Good point. I wrote some rationale for mutability, treating immutability as an obvious option, but this should be revisited. Leigh brought up a similar point - most assets don’t use AUTH_IMMUTABLE, so contract immutability may not be as important as I thought it would be. I think the authorization flags should be removed from the CAP for now.


manifest vs wasm
any reason to update them separately?
As the manifest contains the list of exports, it sounds dangerous to decouple their lifecycles.
This was done intentionally so the manifest could be updated without specifying the entire contract, making the update much cheaper. This isn’t useful for V1 since all we specify is the interface, but could be helpful if we add routing and other features.


re: global flags
I think we need to break down scenarios and have a rationale for this. I am not sure those flags meet requirements.
While we're ramping up smart contracts on Stellar, we probably need a (temporary) way to gradually open up the network to new authors. Like maybe only allow some accounts to publish (many solutions can be done outside of the protocol, assuming validators are all on board).
There are probably questions/concerns over the longer term as well that we should discuss. Like should validators have a way to deal with bad contracts in some way (this can be done outside of the protocol too, but much harder).
If we encounter any issues related to smart contracts that are detrimental to the network, then freezing smart contract state and execution with the new LedgerHeader flags sounds like the right thing to do. A soft launch mechanism would be nice to have so we can identify issues like inaccurate gas pricing, but any mechanism we come up with will be complex. This is something we need to think about some more.

Thanks.
Siddharth

Leigh McCulloch

unread,
May 13, 2022, 2:35:16 PM5/13/22
to Siddharth Suresh, Nicolas Barry, Stellar Developers
Regarding the latest revision, which is: https://github.com/stellar/stellar-protocol/blob/2a27577653f19343c9bb682c80917f2f06919e41/core/cap-0047.md.

typedef opaque WASMCode<>;

The WASMCode has an unbounded size, or rather a max size of approximately 4GB, which seems unnecessary. I assume this type will show up inside transactions. This will be the first type inside transactions that have an unbounded size. By not setting the size we make it ambiguous for all the applications that parse Stellar transactions as to what is a valid tx worth attempting to parse. I imagine we're going to specify some upper limit as a recommendation for the ecosystem of tools that parse XDR, and if we're going to have that recommendation we may as well encode it into the XDR. I think moving that value into the validators config is convenient for validators, but for no one else.

Why are those classic operations?
Classic operations work with very different assumptions fee wise, and as the payloads are much larger in this context, we should avoid it.
Also, this would imply that you cannot manage contracts from smart contracts? I don't see any goals/requirements on that front.
Another way to do this would be to expose "host functions" to perform the same thing, and (possibly) a "default network contract" of sorts that is managed by the network (this could have other uses, see below) so that it's possible to invoke those host functions to spin up contracts on an empty ledger (bootstrap).
I was under the impression that the fee model for classic operations is going to change too, but it sounds like that is a change that I shouldn’t rely on in this CAP. I also thought a classic interface would be simple for users to understand. Using host functions to manage contracts, along with the ability to call contracts without storing them to “bootstrap” like you mentioned would work though. This would allow contracts to manage other contracts as well, if we decide to allow that. One point I’m not sure about yet is how you would submit this initial call to set up a contract. Is it a classic operation? If so, then we still have the issue with the fee model.

If there are host fns for managing contracts, and it is possible to have a script-like contract inside a transaction that executes, I think you'd set up a contract from within one of these transaction scripts. You'd have to write a contract, or use an off the shelf contract, to create your contract. I think there would need to be a way to execute these transaction scripts in classic.

Cheers,
Leigh

Jonathan Jove

unread,
May 17, 2022, 11:46:21 AM5/17/22
to Leigh McCulloch, Siddharth Suresh, Nicolas Barry, Stellar Developers
# Some thoughts on this proposal

## Unique Integer Identifiers

As currently proposed, every contract gets a unique integer contractID. The proposal does not specify it, so I am going to assume that identifiers are drawn from `LedgerHeader.idPool`. The main advantage of this approach is that a contract can be identified using only 8 bytes. The main disadvantage of this approach is that the contractID assigned depends on execution order. Specifically, it is not trivial to parallelize contract creation because it will conflict on idPool. Is this the best approach?

## Account-Contract Pairing

Why should an account own a contract? What happens if an account owns multiple contracts? What exactly does ownership confer? For context, in Ethereum an account can be either externally owned or a contract.

CAP-0050 proposes a model in which Stellar accounts are logically separated from addresses used in contracts. In this model, both public keys and contracts are first class addresses. Philosophically speaking, one would then expect a contract to have no owner. What are the implications of such a model for contract lifecycle?

## UpdateContractOp

While I believe that mutable contracts are valuable, I'm not convinced that we need to support this in the initial version. Proxy contracts are an established way to achieve upgradeability in contracts.

My first concern relates to ownership (see "Account-Contract Pairing" above). Why should the owner always be able to update a contract? Can you change the owner if you want to change who can update it? What are the consequences of this design? This seems unnecessarily prescriptive. For example, if ownership confers the ability to update a contract then contracts with complex update logic will need to disable this in order to implement their own logic.

My next concern relates to updating contracts from a contract. This capability is required for contracts with complex update logic, as already noted. Presumably, this update logic would reside in the contract that you want to update. But what does it mean to update code that is currently running? Does the code only update at the end of the transaction? Or does updating the code terminate contract execution? These are questions that we would need to address in order to generally support updates.

It's not obvious to me that mutable contracts are better than immutable contracts with a fast proxy or a fast routing layer. There is always time for optimizations and improvements later, but it's more important to quickly deploy something that works.

## RemoveContractOp

My concerns here are similar to my concerns about UpdateContractOp regarding ownership. Why should the owner always be able to delete a contract? What about the case where a contract wants to control when it can be deleted and/or by whom?

The same question about deleting code that is currently running applies? Does it run to completion then delete? Or does deleting the code terminate contract execution?

## Contract Metadata

I'm not convinced that we need to support this in the initial version. It would be helpful if the design rationale went into greater detail about the advantages and disadvantages of including contract metadata. For example, suppose we don't include contract metadata. Is it possible to identify what interfaces a contract supports? See https://eips.ethereum.org/EIPS/eip-165. What would be the consequences of this approach? Would contract metadata have any advantages over a EIP-165 approach (or any other you can think of)?

I do think that contract metadata as a fast router could be very useful, but I see that as an optimization as noted above. Would there be any compatibility issues if we were to add this later?

## Maximum Contract Size

Should the maximum contract size even be configurable? The maximum contract size is really a reflection of technical limitations: burden on the overlay, the cost of loading WASM, etc. In the unlikely case that we did need to switch to a variable maximum size later, we could always do so by changing `typedef opaque WASMCode<MAX_CODE_SIZE>` to `typedef opaque WASMCode<>` and adding the appropriate checks, or by introducing a new type of contract code entry if we were concerned about backwards compatibility.

It is always possible to build arbitrarily large contracts by delegating functionality to other contracts. For example, a contract could be a thin wrapper that only forwards calls to other contracts. This design automatically forces deployers to "upload their contract in smaller chunks" if the contract is large, without requiring any native protocol support.

## Global Flags

We should consider reusing the enum values from the liquidity pool flags. They were intended to be removed if they were never needed, see https://github.com/stellar/stellar-protocol/blob/master/core/cap-0038.md#ledger-header-flags.

# Some thoughts on other comments

## Gradually Open Up the Network

Nicolas suggests "While we're ramping up smart contracts on Stellar, we probably need a (temporary) way to gradually open up the network to new authors." I'm not certain that this achieves the network goals. The most important thing for success will be rapid adoption, and people will not adopt anything if they aren't even able to deploy. If we are concerned that our design or implementation is too risky, then we probably need to do more work before opening access to anyone.

Siddharth's flags approach, which would be used by validators in response to a crisis, is probably a better fit.

# One nitpick

I think something went wrong with the formatting of your subsection titled "Create/update/delete operations are used to manage a contract instead of a single operation".

--
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.

Jonathan Jove

unread,
May 17, 2022, 5:15:33 PM5/17/22
to Leigh McCulloch, Siddharth Suresh, Nicolas Barry, Stellar Developers
It has been brought to my attention that my comments about "Maximum Contract Size" are ambiguous. To clarify, I'm advocating for a fixed maximum size that is not configurable (probably somewhere between 16k and 32k).

Siddharth Suresh

unread,
May 17, 2022, 8:15:58 PM5/17/22
to Jonathan Jove, Leigh McCulloch, Nicolas Barry, Stellar Developers
Thanks for the feedback. Please see my responses below.

## Maximum Contract Size

Should the maximum contract size even be configurable? The maximum contract size is really a reflection of technical limitations: burden on the overlay, the cost of loading WASM, etc. In the unlikely case that we did need to switch to a variable maximum size later, we could always do so by changing `typedef opaque WASMCode<MAX_CODE_SIZE>` to `typedef opaque WASMCode<>` and adding the appropriate checks, or by introducing a new type of contract code entry if we were concerned about backwards compatibility.
 
To Leigh's point, I don't think it should be unbounded (I wasn't sure what the bound should be so I left it blank). I think an upper bound should be specified in the XDR (64k? 128k?), but the maximum should be controlled by the validators. This allows the validators to adjust the max if needed easily.

I agree that "the maximum contract size is really a reflection of technical limitations". We still need to estimate the size of the contracts based on those limitations. The advantage with making the maximum configurable is that we can easily fix things if we choose the wrong value initially (either too high or too low).

## Unique Integer Identifiers

As currently proposed, every contract gets a unique integer contractID. The proposal does not specify it, so I am going to assume that identifiers are drawn from `LedgerHeader.idPool`. The main advantage of this approach is that a contract can be identified using only 8 bytes. The main disadvantage of this approach is that the contractID assigned depends on execution order. Specifically, it is not trivial to parallelize contract creation because it will conflict on idPool. Is this the best approach?

Yes, the idea here was to use LedgerHeader.idPool (I'll make sure to add this to the CAP if we stick with this approach). To avoid being dependent on execution order, we could create a Hash ID similar to the ClaimableBalanceID, but I'm not sure if this is necessary. Do we need to parallelize contract creation? And if we needed to parallelize, would it be that difficult to make this work? I want to make sure a more complex identifier scheme is worth doing here.

## Account-Contract Pairing

Why should an account own a contract? What happens if an account owns multiple contracts? What exactly does ownership confer? For context, in Ethereum an account can be either externally owned or a contract.
  
CAP-0050 proposes a model in which Stellar accounts are logically separated from addresses used in contracts. In this model, both public keys and contracts are first class addresses. Philosophically speaking, one would then expect a contract to have no owner. What are the implications of such a model for contract lifecycle? 

This is a good point, and it's clear that the contract ownership model in CAP-0047 will not work for the authorization schemes discussed. For this CAP to work with CAP-0050, the owner should be removed from ContractCodeEntry, along with the UpdateContractOp and RemoveContractOp. The contract itself can control who can update and remove the contract. This would require the contract creator to write an update and/or remove method into the contract, along with us providing a host function to update/remove the ContractCodeEntry.

Related to this, I think we should also include a CreateContractTransaction, (similar to InvokeContractTransaction in CAP-0050). Without this, you would need to use the RunWasmTransaction from CAP-0050, along with nested WASM code, which I feel will be confusing for users.

## UpdateContractOp 
My next concern relates to updating contracts from a contract. This capability is required for contracts with complex update logic, as already noted. Presumably, this update logic would reside in the contract that you want to update. But what does it mean to update code that is currently running? Does the code only update at the end of the transaction? Or does updating the code terminate contract execution? These are questions that we would need to address in order to generally support updates.
 
## RemoveContractOp
The same question about deleting code that is currently running applies? Does it run to completion then delete? Or does deleting the code terminate contract execution? 

I wasn't aware that it would be possible to interact with a contract while it is running. I assumed it would have to return before anything else is done to it, so I might be missing something about how contract execution works. Also, If the update and remove logic are put into the contract instead of an operation, then you wouldn't be able to call the update method while a different method in the same contract is running right? So any pending invocations of the contract will have to wait until any previous invocations are done. I think this is something we should enforce.

Having said that, I think it's fine to not include support for mutable contract code initially, and then provide the necessary host functions for contract updates at a later date.


## Contract Metadata

I'm not convinced that we need to support this in the initial version. It would be helpful if the design rationale went into greater detail about the advantages and disadvantages of including contract metadata. For example, suppose we don't include contract metadata. Is it possible to identify what interfaces a contract supports? See https://eips.ethereum.org/EIPS/eip-165. What would be the consequences of this approach? Would contract metadata have any advantages over a EIP-165 approach (or any other you can think of)?

I do think that contract metadata as a fast router could be very useful, but I see that as an optimization as noted above. Would there be any compatibility issues if we were to add this later?
 
I'll expand on this section in the CAP. We could use host functions that validate the contract argument types, and contract writers can write methods that validate a set of arguments (Ex. pay(src, dest, amount) could be accompanied by validate_pay_args(src, dest, amount)). Just an example, but I'll need to think about this some more. The nice thing about the metadata is it allows the validation to occur in the protocol, avoiding a WASM VM. 

The metadata itself is not required for an initial version. As contracts are published on the network, contract writers can instead provide documentation on the interface externally, which I think would be sufficient initially. The only risk here is using incorrect arguments and losing fees on a failed transaction. I do think some way to identify the interface is required for the users sake long term. 

Adding the fast router or even the metadata union later on would be easy, so I'm not opposed to removing the metadata from the initial version. If we decide to keep the metadata though, I need to answer the question about how the metadata is updated (it was previously done with UpdateContractOp, which is being removed).

## Global Flags

We should consider reusing the enum values from the liquidity pool flags. They were intended to be removed if they were never needed, see https://github.com/stellar/stellar-protocol/blob/master/core/cap-0038.md#ledger-header-flags.

Interesting. Sounds like a good idea. I'll update the CAP.

Siddharth

Leigh McCulloch

unread,
May 18, 2022, 12:33:33 PM5/18/22
to Siddharth Suresh, Jonathan Jove, Nicolas Barry, Stellar Developers
## Contract Metadata

I'm not convinced that we need to support this in the initial version. It would be helpful if the design rationale went into greater detail about the advantages and disadvantages of including contract metadata. For example, suppose we don't include contract metadata. Is it possible to identify what interfaces a contract supports? See https://eips.ethereum.org/EIPS/eip-165. What would be the consequences of this approach? Would contract metadata have any advantages over a EIP-165 approach (or any other you can think of)?

I do think that contract metadata as a fast router could be very useful, but I see that as an optimization as noted above. Would there be any compatibility issues if we were to add this later?
 
I'll expand on this section in the CAP. We could use host functions that validate the contract argument types, and contract writers can write methods that validate a set of arguments (Ex. pay(src, dest, amount) could be accompanied by validate_pay_args(src, dest, amount)). Just an example, but I'll need to think about this some more. The nice thing about the metadata is it allows the validation to occur in the protocol, avoiding a WASM VM. 
 
We don't actually need hostfns to validate these things. In the Rust SDK (rs-stellar-contract-sdk) all type conversions for data in contracts are checked. The contract is required to unwrap i64 values into meaningful types and objects and the contract must actively choose to handle errors or abort.
Screen Shot 2022-05-17 at 8.01.32 PM.png

The metadata itself is not required for an initial version.

I think it is ok to kick it down the road, but it's worth noting that we're motivated to have a manifest as a way to declare the types of wasm functions for some basic sanity checking, and as a way for contract developers to share their interface with each other. I think it would be better if the manifest is designed from the protocol side first because the contract side will just be a materialization of that in some user-friendly format. If core doesn't have a manifest, we may still create one in the SDK and it would be great to coordinate so we don't end up with an SDK manifest that is awkward for the protocol.

Cheers,
Leigh


Jonathan Jove

unread,
May 18, 2022, 1:46:41 PM5/18/22
to Leigh McCulloch, Siddharth Suresh, Nicolas Barry, Stellar Developers
> The advantage with making the maximum configurable is that we can easily fix things if we choose the wrong value initially (either too high or too low).

Is it even safe to lower the maximum size? Consider a contract that acts as a factory for other contracts. If the generated contract has too large of a size after the maximum size is lowered, then the factory contract is broken.


> Yes, the idea here was to use LedgerHeader.idPool (I'll make sure to add this to the CAP if we stick with this approach). To avoid being dependent on execution order, we could create a Hash ID similar to the ClaimableBalanceID, but I'm not sure if this is necessary. Do we need to parallelize contract creation? And if we needed to parallelize, would it be that difficult to make this work? I want to make sure a more complex identifier scheme is worth doing here.

I'm not certain we need to parallelize contract creation, but it would potentially be nice to have that option since I'd expect it to be relatively slow--it's writing a lot of data. Deployment costs on other blockchains can be really high, so this could be an important factor.

Anything we build that parallelizes trivially will be a lot easier to deal with because it won't require any bespoke engineering efforts that are (a) risky and (b) time consuming.

> I wasn't aware that it would be possible to interact with a contract while it is running. I assumed it would have to return before anything else is done to it, so I might be missing something about how contract execution works. Also, If the update and remove logic are put into the contract instead of an operation, then you wouldn't be able to call the update method while a different method in the same contract is running right? So any pending invocations of the contract will have to wait until any previous invocations are done. I think this is something we should enforce.

Contracts can re-enter. For example, it is possible for A::x to call B::y to call A::x. So it is definitely possible to interact with a contract while it's running.

But that's not what I was talking about in my previous post. Consider a contract C. The contract maintains a "deployer" which can be set by calling C::setDeployer (this function fails if called by anyone other than the current deployer). There is also a function C::redeploy (this function fails if called by anyone other than the current deployer) which causes the contract to update its code. The contract is already running when it wants to update its code--it has to be, because it's making the decision about whether the code can be updated.

You could imagine a similar situation for deleting the code. For reference you can compare against Ethereum's SELFDESTRUCT opcode (see https://www.ethervm.io/#FF).

> Interesting. Sounds like a good idea. I'll update the CAP.

In retrospect, I'm thinking it might be annoying to do this in a single protocol change. Maybe we want to add the new flags, then later remove the old flags?

Leigh McCulloch

unread,
Jun 8, 2022, 9:08:17 PM6/8/22
to Siddharth Suresh, Stellar Developers
Hi Siddharth,

union SCType switch (SCValType type)
{
case SCV_U63:
case SCV_U32:
case SCV_I32:
case SCV_STATIC:
    void;
case SCV_OBJECT:
    SCObjectType objType;
case SCV_SYMBOL:
case SCV_BITSET:
case SCV_STATUS:
    void;
};
struct FunctionSignature
{
    SCSymbol function;
    SCType returnType;
    SCType argTypes<10>;
}

There's a problem with the definition of SCType in that it doesn't distinguish between the different types that can be contained within an SCV_STATIC. This is the same problem that resulted in the SCObjectType being embedded in the SCV_OBJECT case. There probably needs to be an enum in the SCV_STATIC case with sub-types void and bool, something like:

enum SCStaticType
{
    SCST_VOID = 0,
    SCST_BOOL = 1
};


I know it is unlikely this extension will be included in the protocol, but worth addressing if we're going to keep it in the CAP for future consideration.

Cheers,
Leigh


Siddharth Suresh

unread,
Jun 9, 2022, 10:55:22 AM6/9/22
to Leigh McCulloch, Stellar Developers

There's a problem with the definition of SCType in that it doesn't distinguish between the different types that can be contained within an SCV_STATIC. This is the same problem that resulted in the SCObjectType being embedded in the SCV_OBJECT case. There probably needs to be an enum in the SCV_STATIC case with sub-types void and bool, something like:

enum SCStaticType
{
    SCST_VOID = 0,
    SCST_BOOL = 1
};

Thanks Leigh, that's a good point. It looks like SCStaticType would just be used to group SCStatic values together for validation (in this case SCS_TRUE and SCS_FALSE). Have we considered pulling the bool values out of SCStatic into a new SCValType? That would get rid of the need for a SCStaticType.

Leigh McCulloch

unread,
Jun 9, 2022, 11:12:21 AM6/9/22
to Siddharth Suresh, Stellar Developers
We have limited tag space and room for only one more tag, however there is a large amount of static space and we plan to add more static types. For example we plan to add a STATIC_NONE value for options: https://github.com/stellar/rs-stellar-contract-env/issues/105. It wouldn't be practical to make all static types there own tag.

Siddharth Suresh

unread,
Jun 9, 2022, 11:39:36 AM6/9/22
to Leigh McCulloch, Stellar Developers
Ah yeah I forgot to consider the limited tag space. Using SCStaticType works here then to group values for validation.
Reply all
Reply to author
Forward
0 new messages