-
Notifications
You must be signed in to change notification settings - Fork 72
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
Payload exploration #120
base: main
Are you sure you want to change the base?
Payload exploration #120
Conversation
token_with_payload/src/contract.rs
Outdated
|
||
verify_and_consume_nonce(&e, &from_id, &nonce); | ||
|
||
crate::auth::check_auth::<TokenPayload>( |
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.
Is the idea to use/reuse different payloads for different functions that need auth? I suppose you omitted that from the PR, as only this fn uses the new auth.
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.
It's exploratory work. I learned what I needed from this part so I didn't do the rest of the work (as noted, I expect everything to change). But in practice every function will use the same new auth.
fn payload( | ||
e: Env, | ||
function: Symbol, | ||
args: Vec<RawVal>, |
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.
For me the main value of the initial proposal for having fn_payload
was that we have a typed interface to contract fn we need to sign. Here it's still not clear what the args should be, which I think is on of the main issues with signing fns. Or is this complimentary to the initial proposal and contracts still would introduce fn_payload for fns with non-trivial args mapping?
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.
There's not really any easy way to do the f_payload
thing as noted in the PR description. I agree typed interfaces are nice, and I would prefer that. For the sake of this exploration, I went this way because it meant I could move fast without solving other problems (like length limits). The real implementation might look different.
#[cfg_attr(feature = "export", contractimpl)] | ||
#[cfg_attr(not(feature = "export"), contractimpl(export = false))] |
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.
Just an fyi, this doesn't do anything anymore. contractimpl
always exports.
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.
Good to know. I copied and pasted this from the token example, so it should be removed there too.
…and extend implementation to xfer
Just updated this in a variety of ways:
There are still a couple of pieces missing from this, notably:
|
Added the missing pieces described above and fixed a couple of bugs. A wallet or other downstream system would use this as follows:
I know that this flow is pretty complex, but I think it covers an enormous number of cases including interesting cases that involve functions that forward some-but-not-all signatures to other functions. See liquidity_pool_router_with_payload This is basically concept-complete at this point so feedback on the idea would be great. Sorry that the implementation is largely hideous, but I'm confident it could be cleaned up. |
I forgot to mention that the wallet needs to do some verification on the payloads it receives before requesting signatures. Let me think about exactly what needs to be checked. |
); | ||
|
||
let mut callstack_a = callstack.clone(); | ||
callstack_a.push_back((deposit_a.token.clone(), symbol!("xfer"))); |
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.
Should call stack a contain this contract (i.e. liquidity pool)?
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.
Great question. I think it could go either way. Contracts would probably be mildly cheaper if we defined "call stack" to mean everything before this function--this means the simple case of passing a signature to the first contract incurs no call stack cost since it would be empty. But the whole system would probably be slightly less surprising if we defined "call stack" to mean everything including this function. What do you think?
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.
Isn't call stack here deposit
->xfer
,so no matter which option do we choose deposit
happens before xfer
and hence should be included no matter which option we choose? IIUC our goal should be to distinguish 'xfer' from 'deposit->xfer' in order to prevent frontrunning messing up things, right?
} | ||
} | ||
|
||
pub trait PayloadTrait { |
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.
Would this trait be a part of a library? I don't see it used in auth now.
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.
Yes I think so. It would probably be in the soroban-auth crate since implementing this trait would be part of implementing standard auth.
function: Symbol, | ||
args: Vec<RawVal>, | ||
callstack: Vec<(BytesN<32>, Symbol)>, | ||
) -> Map<Symbol, (Identifier, SaltedSignaturePayload)> { |
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.
This looks nice, pretty close to what I've imagined, albeit a bit verbose. Maybe we could provide a simple implementation for single-signature contracts, while allowing more complex contracts to be flexible.
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.
I'm very open to having multiple versions of this, and I actually think that's one of the major strengths of this approach. For example, we could add a function fn payload_v(e: Env) -> Symbol
(payload_v
is short for "payload version") that returns an identifier for what payload function to call. We could even make that fn payload_v(e: Env, function: Symbol) -> Symbol
if we wanted to allow it to vary by function. Then we could have a couple standards at different complexity levels, and the wallet would just invoke the right one based on the return value of payload_v
.
.get_unchecked(symbol!("sig")) | ||
.unwrap(); | ||
|
||
let to_verify = SaltedSignaturePayload::V0(SaltedSignaturePayloadV0 { |
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.
I think this isn't quite right. I'm pretty sure I would need to add to_forward_a
and to_forward_b
to this signature payload.
What
This PR contains a still-in-progress exploration of having contracts expose a
payload
function to facilitate signing in wallets and other downstream systems. To make development as fast as possible, I've replicated/modified auth functionality in this crate rather than working in soroban-auth. The final implementation of course would be in soroban-auth.Earlier discussions of this idea used names like
f_payload
but in general those names don't comply with the 10 character symbol requirement. The universalpayload
function is the reaction to that issue. The signature ofpayload
is almost certain to change as this exploration develops.This also incorporates an idea from @dmkozh about putting all signatures into a single argument at the end of the argument list. Exploration on this part is very preliminary, and is almost certain to change entirely.
cc @leighmcculloch who requested this PR
Why
Making signing easier is a huge piece of our work in the last several weeks.
Known limitations
Too many to enumerate