-
Notifications
You must be signed in to change notification settings - Fork 43
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
Add opt in reentrancy to soroban #1491
base: main
Are you sure you want to change the base?
Changes from all commits
43a3ed8
efd3537
22f5770
d3bc92b
450cbf3
abe39c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
[toolchain] | ||
channel = "stable" | ||
channel = "1.81" | ||
targets = ["wasm32-unknown-unknown"] | ||
components = ["rustc", "cargo", "rustfmt", "clippy", "rust-src"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1560,6 +1560,61 @@ | |
], | ||
"return": "Val", | ||
"docs": "Calls a function in another contract with arguments contained in vector `args`, returning either the result of the called function or an `Error` if the called function failed. The returned error is either a custom `ContractError` that the called contract returns explicitly, or an error with type `Context` and code `InvalidAction` in case of any other error in the called contract (such as a host function failure that caused a trap). `try_call` might trap in a few scenarios where the error can't be meaningfully recovered from, such as running out of budget." | ||
}, | ||
{ | ||
"export": "1", | ||
"name": "call_reentrant", | ||
"args": [ | ||
{ | ||
"name": "contract", | ||
"type": "AddressObject" | ||
}, | ||
{ | ||
"name": "func", | ||
"type": "Symbol" | ||
}, | ||
{ | ||
"name": "args", | ||
"type": "VecObject" | ||
} | ||
], | ||
"return": "Val", | ||
"docs": "Calls a function in another contract with arguments contained in vector `args`. If the call is successful, returns the result of the called function. Traps otherwise. This functions enables re-entrancy in the immediate cross-contract call.", | ||
"min_supported_protocol": 21 | ||
}, | ||
{ | ||
"export": "2", | ||
"name": "try_call_reentrant", | ||
"args": [ | ||
{ | ||
"name": "contract", | ||
"type": "AddressObject" | ||
}, | ||
{ | ||
"name": "func", | ||
"type": "Symbol" | ||
}, | ||
{ | ||
"name": "args", | ||
"type": "VecObject" | ||
} | ||
], | ||
"return": "Val", | ||
"docs": "Calls a function in another contract with arguments contained in vector `args`, returning either the result of the called function or an `Error` if the called function failed. The returned error is either a custom `ContractError` that the called contract returns explicitly, or an error with type `Context` and code `InvalidAction` in case of any other error in the called contract (such as a host function failure that caused a trap). `try_call` might trap in a few scenarios where the error can't be meaningfully recovered from, such as running out of budget. This functions enables re-entrancy in the immediate cross-contract call.", | ||
"min_supported_protocol": 21 | ||
}, | ||
{ | ||
"export": "3", | ||
"name": "set_reentrant", | ||
"args": [ | ||
{ | ||
"name": "enabled", | ||
"type": "Bool" | ||
} | ||
], | ||
"return": "Void", | ||
"docs": "Enables the current contract to specify the reentrancy rules.", | ||
"min_supported_protocol": 21 | ||
Comment on lines
+1607
to
+1617
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the idea with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Set reentrant just instructs the host that the contract in this context (or better, currently it sets the flag on the whole host). The current implementation requires two specialized host functions to have the callparam option with the reentry enabled (while call and try_call always have it disabled), and |
||
} | ||
] | ||
}, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In an outgoing call graph that looks like this:
Does it mean 1️⃣ that reentry into
A
is only allowed fromB
? For example:Or 2️⃣ that reentry into
A
is allowed from any contract that is executing further down the stack thanA
's call out? For example:I think 2️⃣ fits better with narrative on what it means to call out to another contract. What that other contract does, whether it is a monolith or a micro component is irrelevant to the calling contract. The calling contract merely needs to acknowledge that it is capable to handle being re-entered during the call. By who, is mostly irrelevant.
Are there cases where the who is important?
If I'm completely misunderstanding, please correct me 😄.
cc @dmkozh @sisuresh
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this was also partially discussed on the thread. Currently the implementation is fairly unsafe because when reentrancy is enabled the called contract can be reentrant on any contract down the stack. This means that scenario 2 (which is what I think we should go for) is possible, but it also makes possible a scenario where e.g D is reentrant on B which didn't have reentrancy enabled.