From fe5fb0c430c351ddc63b308a0bb2e36c8c7d69e7 Mon Sep 17 00:00:00 2001 From: Ludovic Galabru Date: Wed, 12 Feb 2020 15:50:25 -0500 Subject: [PATCH] Update SIP002 --- sip/sip-002-smart-contract-language.md | 103 +++++++++++++++++-------- 1 file changed, 70 insertions(+), 33 deletions(-) diff --git a/sip/sip-002-smart-contract-language.md b/sip/sip-002-smart-contract-language.md index 3693147e0..6903810fa 100644 --- a/sip/sip-002-smart-contract-language.md +++ b/sip/sip-002-smart-contract-language.md @@ -98,48 +98,85 @@ any type. ## Inter-Contract Calls A smart contract may call functions from other smart contracts using a -`(contract-call!)` function. This function accepts a function name and -the smart contract's _identifier_ as input. A smart contract's -identifier is a hash of the smart contract's definition, represented -as a Stacks address with a specific "smart contract" version byte. The -smart contract identifier is a _principal_. +`(contract-call?)` function. -For example, to call the function `register-name` in a smart contract, -you would use: - -```scheme -(contract-call! - 'SC3H92H297DX3YDPFHZGH90G8Z4NPH4VE8E83YWAQ - 'register-name - name-to-register) -``` - -This function returns a boolean-- the return value of the called smart +This function returns a Response type result-- the return value of the called smart contract function. Note that if a called smart contract returns an `err` type, it is guaranteed to not alter any smart contract state whatsoever. Of course, any transaction fees paid for the execution of that function will not be returned. +We distinguish 2 different types of `contract-call?`: + +* Static dispatch: the callee is a known, invariant contract available +on-chain when the caller contract is being deployed. In this case, the +callee's principal is provided as first argument, followed by the name +of the method and its arguments: + +```scheme +(contract-call? + 'SC3H92H297DX3YDPFHZGH90G8Z4NPH4VE8E83YWAQ.registrar + register-name + name-to-register) +``` + +This approach must always be preferred, when adequat. +It makes static analysis easier, and eliminates the +potential for reentrancy bugs when the contracts are +being published (versus when being used). + +* Dynamic dispatch: the callee is passed as an argument, and typed +as a trait reference (). + +```scheme +(define-public (swap (token-a ) + (amount-a uint) + (owner-a principal) + (token-b ) + (amount-b uint) + (owner-b principal))) + (begin + (unwrap! (contract-call? token-a transfer-from? owner-a owner-b amount-a)) + (unwrap! (contract-call? token-b transfer-from? owner-b owner-a amount-b)))) +``` + +Traits can either be locally defined: + +```scheme +(define-trait can-transfer-tokens ( + (transfer-from? (principal principal uint) (response uint))) +``` + +Or imported from an existing contract: + +```scheme +(use-trait can-transfer-tokens + 'SC3H92H297DX3YDPFHZGH90G8Z4NPH4VE8E83YWAQ.contract-defining-trait.can-transfer-tokens) +``` + +Looking at trait conformance, callee contracts have two different paths. +They can either be "compatible" with a trait by defining methods +matching some of the methods defined in a trait, or explicitely declare +conformance using the `impl-trait` statement: + +```scheme +(impl-trait 'SC3H92H297DX3YDPFHZGH90G8Z4NPH4VE8E83YWAQ.contract-defining-trait.can-transfer-tokens) +``` + +Explicit conformance should be prefered when adequat. +It acts as a garde-fou and help the static analysis detecting +deviations in method signatures before contract deployment. + The following limitations are imposed on contract calls: -1. No dynamic dispatch. At the time of the smart contract creation, - any contracts being called must be specified. Future designs may - enable this by allowing contract principals to be supplied as - function arguments, however, on initial release, we believe - dynamic invocation to be too dangerous to support. -2. Called smart contracts _must_ exist at the time of creation. -3. No cycles may exist in the call graph of a smart contract. This +1. On static dispatches, callee smart contracts _must_ exist at the time of creation. +2. No cycles may exist in the call graph of a smart contract. This prevents recursion (and re-entrancy bugs). Such structures can be detected with static analysis of the call graph, and will be rejected by the network. - -The language described here only allows for eager binding of smart -contract function calls-- this makes static analysis easier, and -eliminates the potential for reentrancy bugs. A key benefit of the -static analyzability of this smart contracting language is that _all_ -functions that can possibly be called from a given transaction can be -known _a priori_ so that a user can be warned about all side effects -before signing a transaction. +3. `contract-call?` are for inter-contract calls only. Situations + where the caller is also the callee will result in abortion of + the ongoing transaction. ## Principals and Owner Verification @@ -166,7 +203,7 @@ priori which functions a given smart contract will ever call. Another global variable, `contract-caller`, _does_ change during inter-contract calls. In particular, `contract-caller` is the contract -principal corresponding to the most recent invocation of `contract-call!`. +principal corresponding to the most recent invocation of `contract-call?`. In the case of a "top-level" invocation, this variable is equal to `tx-sender`. Assets in the smart contracting language and blockchain are @@ -322,7 +359,7 @@ Example: 12234) -> returns owner principal of name represent by integer 12234 ``` -Just as with the `(contract-call!)` function, the map name and contract +Just as with the `(contract-call?)` function, the map name and contract principal arguments must be constants, specified at the time of publishing.