feat(abstract-utxo): add zec shielded psbt decode and recipient resolution support - #9642
Conversation
|
|
c0b5ca0 to
d3a9a4f
Compare
| /** | ||
| * Custom change wallet xpubs, when the transaction spends to a custom change wallet. Outputs | ||
| * matching these keys are classified as change, not recipients — matching how | ||
| * `explainPsbtWasm` treats them. | ||
| */ | ||
| customChangeXpubs?: Triple<string>; |
There was a problem hiding this comment.
we don't need to support custom change wallets, let's leave this unimplemented and fail hard if a custom change wallet is configured
| try { | ||
| return fixedScriptWallet.ZcashUnifiedAddress.parse(address, network); | ||
| } catch (e) { | ||
| return undefined; |
There was a problem hiding this comment.
we should prefer failing hard instead
There was a problem hiding this comment.
Failing hard now
| address: string, | ||
| param?: { anyFormat?: boolean; allowLightning?: boolean } | boolean | ||
| ): boolean { | ||
| const unifiedAddress = tryParseUnifiedAddress(address, this.name as 'zec' | 'tzec'); |
There was a problem hiding this comment.
this seems to be the only site where we use the undefined returning func, I'd rather inline the catch here
There was a problem hiding this comment.
catching the error now
| // `ZcashBitGoPsbt.fromBytes` signals v6 (Ironwood) bytes with a plain Error (not a | ||
| // WasmUtxoError) telling the caller to use `ZcashIronwoodBitGoPsbt.fromBytes` instead — | ||
| // see its doc comment. Fall back for that message as well as wasm-layer errors. | ||
| if (isWasmUtxoError(e) || (e instanceof Error && e.message.includes('v6 (Ironwood)'))) { |
There was a problem hiding this comment.
This is a brittle way to identify errors. We have a more structured system for categorizing errors in wasm-utxo already, please check if we can already use it. If not, submit a change for wasm-utxo so we have a proper error code here instead and let's fix it in a follow-up.
There was a problem hiding this comment.
Using the new ZcashBitGoPsbt.fromBytes which automatically detects the version and returns the right psbt instance
d3a9a4f to
14d690e
Compare
14d690e to
70ffbd0
Compare
| import { resolvePsbtRecipients, PsbtRecipient } from './recipients'; | ||
|
|
||
| export class Zec extends AbstractUtxoCoin { | ||
| readonly name: UtxoCoinName = 'zec'; |
There was a problem hiding this comment.
| readonly name: UtxoCoinName = 'zec'; | |
| readonly name: 'zec' | 'tzec' = 'zec'; |
this should get rid of the as 'zec' | 'tzec' casts, please try
| // Raw script inherently transparent. | ||
| return 'transparent' as const; |
| // Not a unified address: the ordinary transparent address-decoding path handles it. | ||
| return 'transparent' as const; |
There was a problem hiding this comment.
I don't agree with that
ZcashUnifiedAddress.parse can fail for many different reasons, I don't think it's fair to assume transparent as a good default here
we should try to parse it as a transparent address first instead, return transparent on success, and then try to parse it as a Unified address and propagate the error if it is nothing recognizable
| * only be spent shielded, one carrying only a transparent receiver only transparently, one | ||
| * carrying both is ambiguous, and a mix of shielded and transparent recipients is rejected. | ||
| */ | ||
| getUnifiedRecipientPreference(txParams: { |
There was a problem hiding this comment.
I don't see any tests
move this to a standalone func to recipients.ts and add better tests for it
you can also clean up the signature
getUnifiedRecipientPreference(
name: 'zec' | 'tzec',
recipients: { address: string | undefined; }[] // amount isn't actually used
)
| /* Zcash-only: how to resolve a Unified Address recipient ('shielded' or transparent) */ | ||
| unifiedRecipientPreference: t.string, |
There was a problem hiding this comment.
use a union of literals here
| * Orchard/Ironwood receiver (a shielded output); any other value (or omission) resolves it to | ||
| * its transparent receiver. | ||
| */ | ||
| unifiedRecipientPreference?: string; |
There was a problem hiding this comment.
use 'shielded' | 'transparent' here
| * whose address space needs additional context to resolve — e.g. Zcash Unified Addresses, | ||
| * which resolve differently depending on `unifiedRecipientPreference`. | ||
| */ | ||
| resolveOutputScript(address: string, unifiedRecipientPreference?: string): Uint8Array { |
There was a problem hiding this comment.
weak string type
the optional argument here is only useful for zcash and leaks into general AbstractUtxo, we should look for a better solution
| export function fromExtendedAddressFormatToScript( | ||
| extendedAddress: string, | ||
| coinName: UtxoCoinName, | ||
| resolveScript?: (address: string, coinName: UtxoCoinName) => Uint8Array |
There was a problem hiding this comment.
this looks a bit problematic, let's find a better solution here.
let's split up the PR and discuss these changes later
do a separate PR that adds
getUnifiedRecipientPreference(
name: 'zec' | 'tzec',
recipients: { address: string | undefined; }[] // amount isn't actually used
)
only
as discussed here
https://github.com/BitGo/BitGoJS/pull/9642/changes#r3965967895
Ticket: CSHLD-1640