-
-
Save paullinator/c37efc137f50fd7e11e6e4ffca7b0a94 to your computer and use it in GitHub Desktop.
| class KeyTool { | |
| static deriveViewKey (seedBytesHex: string): string | |
| static deriveSpendingKey: (seedBytesHex: string): string | |
| } | |
| class AddressTool { | |
| static deriveShieldedAddress (viewingKey: string): string | |
| static deriveTransparentAddress (viewingKey: string): string | |
| static isValidShieldedAddress (address: string): boolean | |
| static isValidTransparentAddress (address: string): boolean | |
| } | |
| interface InitializerConfig { | |
| host: string | |
| port: number | |
| fullViewingKey: string | |
| // alias: ?? | |
| birthdayHeight: number | |
| } | |
| interface WalletBalance { | |
| shieldedAvailable: string | |
| shieldedTotal: string | |
| transparentAvailable: string | |
| transparentTotal: string | |
| } | |
| interface InitializerCallbacks { | |
| onError: (e: Error): void | |
| onTransactionsChanged: (): void | |
| onBalanceChanged: (walletBalance: WalletBalance): void | |
| } | |
| interface SpendInfo { | |
| zatoshi: string | |
| toAddress: string | |
| memo: string | |
| fromAccountIndex: number | |
| spendingKey?: string | |
| } | |
| interface ZcashTransaction { | |
| txId: string | |
| fee: string | |
| value: string | |
| direction: 'inbound' | 'outbound' | |
| toAddress: string | |
| memo?: string | |
| minedHeight: number // 0 for unconfirmed | |
| blockTime: number // UNIX timestamp | |
| } | |
| type PendingTransaction = ZcashTransaction & { | |
| accountIndex: number | |
| expiryHeight: number | |
| cancelled: number | |
| submitAttemps: number | |
| errorMessage?: string | |
| errorCode?: number | |
| createTime: number | |
| } | |
| interface TransactionQuery { | |
| offset?: number | |
| limit?: number | |
| startDate?: number | |
| endDate?: number | |
| } | |
| class Synchronizer { | |
| static init (initializerConfig: InitializerConfig): void | |
| setCallbackHandlers (callbacks: InitializerCallbacks): void | |
| getLatestHeight (): number | |
| getProgress (): number // 0-1 | |
| // getStatus (): ?? | |
| getBalance (): WalletBalance | |
| estimateFee (spendInfo: SpendInfo): string | |
| sendToAddress (spendInfo: SpendInfo): void | |
| start (): void | |
| stop (): void | |
| getPendingTransactions (): PendingTransactions[] | |
| getConfirmedTransactions (query: TransactionQuery): ZcashTransaction[] | |
| } |
I just read back through my feedback above and converted it into suggested edits on the doc. I don't think I can modify the gist so I've included them here as code snippets:
interface InitializerConfig {
// bullet 1. add alias to existing properties
alias: String
}
// bullet 2. split up balance objects
interface WalletBalance {
available: number // always zatoshi
total: number // always zatoshi
}
class TransparentBalance implements WalletBalance {
available: number
total: number
}
class ShieldedBalance implements WalletBalance {
available: number
total: number
}
// bullet 2f. add currency formatter.
class ZecFormatterTool {
static toZecString (zatoshi: number, minDecimals: number, maxDecimals: number): string
static toZec (zatoshi: number): number
static toZatoshi (zec: number): number
}
// bullet 3: split up callbacks
interface SynchronzerChangedCallbacks {
onTransactionsChanged: (): void
onBalanceChanged: (walletBalance: WalletBalance): void
}
interface SynchronizerErrorCallbacks {
// note: hopefully we can subclass errors to match what currently exists: ProcessorError, ChainError, CriticalError, SubmissionError
onError: (e: Error): void
}
// bullet4: optional change: I'm okay with using 0, we'd just have to adjust in the NativeModule, since we use -1
// bullets 5 & 6
interface ZcashTransaction {
// keep memo as bytes (perhaps add a formatter to get it as a UTF-8 string)
memo?: Uint8Array // hopefully, react native can support bytes in some way and we can use whatever format is necessary to achieve that (i.e. maybe just a hex string)
// move accountIndex up to here from PendingTransaction interface
accountIndex: number
}
// bullet 7: we probably need to discuss this one
// bullet 8: status info
enum SynchronizerStatus {
/** Indicates that [stop] has been called on this Synchronizer and it will no longer be used. */
STOPPED,
/** Indicates that this Synchronizer is disconnected from its lightwalletd server. When set, a UI element may want to turn red. */
DISCONNECTED,
/** Indicates that this Synchronizer is actively downloading new blocks from the server. */
DOWNLOADING,
/** Indicates that this Synchronizer is actively validating new blocks that were downloaded from the server. Blocks need to be verified before they are scanned. This confirms that each block is chain-sequential, thereby detecting missing blocks and reorgs. */
VALIDATING,
/** Indicates that this Synchronizer is actively decrypting new blocks that were downloaded from the server. */
SCANNING,
/** Indicates that this Synchronizer is actively enhancing newly scanned blocks with additional transaction details, fetched from the server. */
ENHANCING,
/** Indicates that this Synchronizer is fully up to date and ready for all wallet functions. When set, a UI element may want to turn green. In this state, the balance can be trusted. */
SYNCED
}
// bullet 9 & 10: scanned v. network height and typo correction
class Synchronizer {
// split latest height into two values
getLatestNetworkHeight(): number
getLatestScannedHeight(): number
// correct minor typo
getPendingTransactions (): PendingTransaction[]
}Note: formatter probably not be needed.
Quick notes on action items:
- add alias
- return zatoshi as a string and don't worry about conversion utilities (strings force conversion)
- separate object
- memo : HEX is fine but it does have to be a string
- get status: just return processor info
@paullinator I have a number of questions here, but one thing that strikes me right away is that the paginated interface for transaction retrieval seems fragile with respect to race conditions. If you retrieve a page of results, and then some additional syncing happens in the background, the page boundaries might change as additional transactions involving your wallet are discovered. Do you have a suggestion of how you would want the API provider to address this, as a user of the API?
oh I recall this gist. I think most of it was actually implemented. The paginated was partially but never used.
@nuttycom Databases deal with page boundaries all the time. Blockchains are even easier. Since the starting point of the query is fixed at a certain date (or blockheight), the number of transactions at each page, and which transactions are in each page should stay constant. Other than due to a reorg, there shouldn't be any possibility of a page ever returning different transactions given a specific starting date/height.
However, due to the possiblity of reorgs, it would be recommended that SDK users have a rollback number of blocks to query when it gets a notification of new balances or transaction updates. ie.
Let's assume the api uses blockheight instead of date for simplicity
App has ROLLBACK_BLOCKS = 20
Current block is 500000
App requests transactions from date 0 at start. it gets say 10 transactions at various heights. It considers itself synced up to block 500000
App gets notification of new transactions, it then queries the sdk from blockheight 499980 which would get all updates for any transactions that may have been changed/reorged since the last update.
Comments:
First, this looks great thanks for putting the energy into writing out the typescript interface! A few things stand out after seeing this converted into code that were probably missing from the original document so I've typed out feedback below:
a. Transparent and shielded balances operate differently. One requires downloading, then decrypting blocks locally, the other is effectively an instant API call.
b. In practice, "shielded" and "transparent" funds operate more like two separate coins. If a wallet has "ETH" and "BTC" funds, it might not want one
WalletBalanceobject to represent them both and it might similarly prefer separateBalanceChangedevents for each coin.c. Mixing both into one
WalletBalanceobject seems like it might cause challenges. Particularly when we have info for one but not the other. Or when one has been updated and the other is stale. Especially if the UI shows any "last updated" timestamps.d. Additionally, representing balance as a string seems a bit ambiguous and potentially less precise
e. Using a Long to represent
zatoshiseems less ambiguous and more precise but may require a formatter tool to convert from zatoshi to ZEC and to provide the desired scale and precision.f. The SDK currently has such a formatter and we might want to wrap it in a "Tool". In the SDK, everything is always in Zatoshi until it is displayed to a user, at which point the wallet uses the formatter to convert into the precision shown on-screen (sometimes 3 places, sometimes the max of 8)
InitializerCallbacksseems a little ambiguous. A developer seeing that might wonder what it contains or how it relates to initialization.a. Perhaps we separate these into
Change HandlersandError Handlers, where the former set of callbacks is strictly concerned with receiving updates when anything changes and the latter only handles errors.b. Perhaps the word
InitializerinInitializerCallbackscould be replaced with something likeWalletorSynchronizersince these callbacks could be set separately from initialization (i.e.setErrorHandlers (callbacks: SynchronizerErrorCallbacks): void)minedHeight: number // 0 for unconfirmed- since 0 is a valid block number, we've been using -1 for unconfirmedmemo?: string- a memo isn't always a string, it can be 512 bytes of anything. We may want to future-proof this by letting it stay bytes and then providing conversions. Perhaps using aUint8Arrayif the added complexity is worth it. Otherwise, we can keep it as a string for simplicity but define a way to distinguish between an empty memo and a memo that exists but cannot be represented as a string.accountIndex: number- might need to be moved to the baseZcashTransactionobject because I think this is something that is always known for a transaction and a necessary property to track when using multiple accounts under one seed.estimateFee (spendInfo: SpendInfo): string- We don't have any functionality for this, at the moment. For shielded transactions, it is always a fixed value of 10000 zatoshi (or 0.0001 ZEC). We are currently developing full transparent funds support and I'm unclear on how we'll handle transparent fees in the SDK.getStatus- there is a lot of info available for this. We can iterate and decide on what is useful at this level but what's available currently is (I've bolded the ones that are most useful): Connected, Disconnected, Syncing, Synced, Downloading, Validating, Scanning, Scanned(range: IntRange), Enhancing, Stopped, InitializedgetLatestScannedHeightto more clearly differentiate between the height of the network and the height of the wallet. This information can be approximated fromprogressandstatusbut it's often useful to know exactly which block was processed last.getPendingTransactions (): PendingTransactions[]- typo. should probably be singular:PendingTransaction[]