-
Notifications
You must be signed in to change notification settings - Fork 333
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
Online signer Prototype #403
Conversation
So as I understand it, there are two advantages of using an online signer over the offline signer we've already implemented:
Are we clear on how realistic these advantages are? E.g. is it plausible to support a decent UX for point 1? |
50800f5
to
22e8446
Compare
Good point. So far I thought the desired feature is the ability to override the fields as a human. Do we even want to allow leaving them empty? Is an online signer able to determine the gas limit for all message types on all chains it connects to? |
This is a good start and nice way to combine the two interfaces. Here is some initial feedback on this PR:
|
Thank you for the feedback SImon. I definitely agree on 1 and 4 and was thinking that already when I hit some issues. For 3 adding the modified transaction (not just hash) is a nice one. Maybe Serialize Tx is the simplest interface to implement correctly For 2. Yes, no need for the account number and sequence ever to be passed to an OnlineSigner. I can remove those as optional. For fees... I think the OnlineSigner should implement this - I will do. But maybe the app allows a user to manually override. I will make it the responsibility of the OnlineSigner, but accept a "hint" from the user/app id not needed |
22e8446
to
9402538
Compare
https://github.com/chainapsis/keplr-extension/blob/cosmjs/src/content-scripts/inject/cosmjs-provder.ts I made an initial prototype of online signer support for Keplr and basic example usage based on your PR. For 2 of @webmaster128's opinion, I think that it is better to set sequence, account number field in the SigningCosmWasmClient. If sequence, account number is null, we have to fetch these values from the node in the online signer anyways, and this seems to be a duplicating the same logic. And, it would be better if values like apiUrl, broadcastMode are passed from SigningCosmWasmClient to the online signer |
I think Simon's concern was that we do it maybe here, maybe there, rather than just leaving one place to do it, and I agree. In general, an app doesn't need to think about that stuff and I consider those values part of the signing process, so I moved that to 100% responsibility of the OnlineSigner. I think you do that already in Kepler, no? If this is an issue, we can move them 100% to the app and not allow nulls. The only fields I think should be maybe signer, maybe app is fees. And in this case, I would propose the app can optionally set a |
Yeah, I was thinking about the apiUrl in particular, and this actually ties with dynamically registering chains. I figured there would be some |
Re: broadcast mode, does async (no confirmation) make any sense in a UI? I only see it for automated testing bots aiming for max volume. That leaves us with |
Looking at these prototypes. Thank you.
I am a bit confused by the last two points, as it seems the extension doesn't go online to get info. But when looking at your ChainInfo proposal the extension needs both rpc and rest endpoints. The difference between the Online and Offline versions seems to be I guess my main question is what are the possible functionality modes that Keplr offers. And maybe the Online/Offline distinction doesn't make much sense. Rather we could have |
Just saw how you import branches from monorepos. Very cool! |
After looking more at the sample, I see all the critical work is done in the OfflineSigner:
The only addition seems to be the This is actually quite nice, as we currently hard-code some price in the app and no way to adjust or predict it. However, if this is the only change we really need to make to the API, I have a much simpler solution than interface OfflineFeeSigner extends OfflineSigner {
readonly calculateFee: (chainId: string, gasLimit: number, suggestedFee?: Coin[]) => Promise<Coin[]>
} It seems this is the heart of the current implementation. I could do something like:
This will just call the actual APIs you expose (and not require you to import a bunch of cosmjs code to get this to work). It simplifies the interfaces you need. And it makes it clear where the actual logic goes. I am happy for any amount of logic and processing happening in the extension. And any amount in the app. I would like to keep the injected code as simple as possible. Especially as a version mismatch between the injected cosmjs and the app's cosmjs would not be so great and the less logic injected the better. What do you think of this proposal? |
Initially, I thought that the tx type can be not In current Keplr's
@ethanfrey I'm not sure that I understood this part 100%. Could you clarify this part? |
Thank you for clarifying. From looking at the UI, it is hard to see what is done where exactly.
Okay, if you will be adding the broadcast by the extension in the future (to the node Keplr knows), I would stick with this interface. And when implemented make cosmjs just use that part. I feel rather hesitant to have so much code compiled into the injected code (as imports). Maybe we can trim that down later, but I was wondering if it made sense the functionality.
Sorry, re-reading that is confusing. I wanted to make sure either the app or the signer is responsible for all fields. Does this make sense: The app provides:
The signer provides:
The signer may override:
|
Given your feedback, I would just polish off these types a bit |
@webmaster128 is on vacation and I would like his input for the final types. I see two main options. Option 1 The first one is to just make the relatively minor adjustments to the current OnlineSigner interface as described above: #403 (comment) This would give us: // move more fields to the caller
export interface SignRequest {
// required fields
readonly msgs: readonly Msg[];
readonly chainId: string;
readonly gas: string;
readonly account_number: string | number;
readonly sequence: string | number;
// optionally set by caller, can be overriden by signer
readonly fee_amount?: readonly Coin[];
readonly memo?: string;
}
export interface SignAndBroadcastResult {
// we add this field as well (return the signed tx that was broadcast) - definition in option 2
tx: StdTx;
result: BroadcastTxResult;
}
export interface OnlineSigner {
readonly enable: () => Promise<boolean>;
readonly getAccounts: () => Promise<readonly AccountData[]>;
// pass in broadcast mode when calling
readonly signAndBroadcast: (address: string, request: SignRequest, broadcastMode = BroadcastMode.Block) => Promise<SignAndBroadcastResult>;
} |
Option 2 When looking at the implementation in keplr, I notice they have to pull in a lot of @cosmjs/launchpad code into the injected script to make this work. As the extension does not directly submit the transaction. I would make a new interface tailored to just what the extension actually does. If it starts broadcasting in a later version, we can revive the Here is an alternate design (freezing this PR and replacing it with a new design). It should be easy to update the current Keplr injected script to this new API (just trim down a bit), as I describe above: #403 (comment) We would have a slightly higher-level interface (chain-aware) that can make some tx modifications before signing. // minimal
export interface OfflineSigner {
readonly getAccounts: () => Promise<readonly AccountData[]>;
readonly sign: (address: string, message: Uint8Array, prehashType?: PrehashType) => Promise<StdSignature>;
}
// this can modify data and returns a complete StdTx with a valid Signature
export interface BuildingSigner {
readonly getAccounts: () => Promise<readonly AccountData[]>;
readonly enable: () => Promise<boolean>;
readonly buildAndSign: (address: string, request: SignRequest) => Promise<StdTx>;
}
export interface StdTx {
readonly msg: readonly Msg[];
readonly fee: StdFee;
readonly signatures: readonly StdSignature[];
readonly memo: string | undefined;
}
export interface StdSignature {
readonly pub_key: PubKey;
readonly signature: string;
}
export interface PubKey {
readonly type: string;
// Value field is base64-encoded in all cases
readonly value: string;
} We could use the exact same |
Given that the only clear use-case we have for the The app retains the responsibility of broadcasting and error handling there, which is much simpler than trying to feed the entire error handling from broadcasting a tx from the extension into the app. Option 2 is my preference, and I would be happy to code it when I am back (in one week), but I would like to hear the opinions of @Thunnini and @webmaster128 first. I think this would be an easy adjustment to what you have already built on the cosmjs integration branch. |
Wouldn't it be better if the sequence was optional so it can also be set by the signer?
I do think option 2 is good but personally prefer option 1. |
I scanned most of the discussion now and a core question seems to be still open: which component is responsible for broadcasting a transaction? As long is there is no consensus on this one, all the rest cannot efficiently be worked on. The two basic ways are:
If we want A, then Keplr should do the broadcasting. If we want B, then we can close here and improve the existing OfflineSigner interface.
+1 for this. The
Option A (~ Option 1) creates more flexibility for the signer while Option B (~ Option 2) creates more flexibility in the frontend. So far I did not hear a convincing argument for A/1 over B/2 other than the fact that a frontend can easily trick the user into signing multiple transactions by presenting fake error messages and then broadcasting all of them to perform a payment multiple times. |
Thank you Ethan for proposing this and @Thunnini for all the input. The way of wrapping an OfflineSigner in an OnlineSigner implementation is very neat and will not be forgotten. For now we decided to allow signers to override parts of the sign doc without implementing an OnlineSigner (#432). Thus #316 remains open until we have a very good reason to implement it. Closing here. |
Closes #316
This is my API design for OnlineSigner and a default version that wraps OfflineSigner. Discussed with @willclarktech and @webmaster128 and generally agreed as a decent design for an OnlineSigner that presents a UI.
TODO:
OnlineSigner.signAndBroadcast