Skip to content
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

Scan from user registered block #549

Merged
merged 12 commits into from
Jul 7, 2023
14 changes: 11 additions & 3 deletions frontend/src/pages/AccountReceive.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@
<div class="row justify-start q-col-gutter-md">
garyghayrat marked this conversation as resolved.
Show resolved Hide resolved
<base-input
apbendi marked this conversation as resolved.
Show resolved Hide resolved
v-model.number="startBlockLocal"
@blur="setScanBlocks(startBlockLocal, endBlockLocal)"
@blur="setScanBlocks(startBlockLocal!, endBlockLocal!)"
class="col-xs-12 col-6"
:label="$t('Receive.start-block')"
:rules="isValidStartBlock"
/>
<base-input
v-model.number="endBlockLocal"
@blur="setScanBlocks(startBlockLocal, endBlockLocal)"
@blur="setScanBlocks(startBlockLocal!, endBlockLocal!)"
class="col-xs-12 col-6"
:label="$t('Receive.end-block')"
:rules="isValidEndBlock"
Expand Down Expand Up @@ -86,6 +86,7 @@ import { QForm } from 'quasar';
import { UserAnnouncement, KeyPair, AnnouncementDetail, utils } from '@umbracash/umbra-js';
import { BigNumber, computeAddress, isHexString } from 'src/utils/ethers';
import useSettingsStore from 'src/store/settings';
import useWalletStore from 'src/store/wallet';
import useWallet from 'src/store/wallet';
import { filterUserAnnouncements } from 'src/worker/worker';
import AccountReceiveTable from 'components/AccountReceiveTable.vue';
Expand All @@ -101,6 +102,7 @@ function useScan() {
// Start and end blocks for advanced mode settings
const { advancedMode, startBlock, endBlock, setScanBlocks, setScanPrivateKey, scanPrivateKey, resetScanSettings } =
useSettingsStore();
const { signer, userAddress: userWalletAddress } = useWalletStore();
const startBlockLocal = ref<number>();
const endBlockLocal = ref<number>();
const scanPrivateKeyLocal = ref<string>();
Expand Down Expand Up @@ -178,7 +180,13 @@ function useScan() {
const overrides = { startBlock: startBlockLocal.value, endBlock: endBlockLocal.value };
let allAnnouncements: AnnouncementDetail[] = [];
try {
allAnnouncements = await umbra.value.fetchAllAnnouncements(overrides);
if (!signer.value) throw new Error('signer is undefined');
if (!userWalletAddress.value) throw new Error('userWalletAddress is undefined');
// When private key is provided in advanced mode, we fetch all announcements
if (advancedMode.value && scanPrivateKey.value)
allAnnouncements = await umbra.value.fetchAllAnnouncements(overrides);
else
allAnnouncements = await umbra.value.fetchSomeAnnouncements(signer.value, userWalletAddress.value, overrides);
} catch (e) {
scanStatus.value = 'waiting'; // reset to the default state because we were unable to fetch announcements
throw e;
Expand Down
44 changes: 34 additions & 10 deletions umbra-js/src/classes/Umbra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ import {
} from '../ethers';
import { KeyPair } from './KeyPair';
import { RandomNumber } from './RandomNumber';
import { blockedStealthAddresses, getEthSweepGasInfo, lookupRecipient, assertSupportedAddress } from '../utils/utils';
import {
blockedStealthAddresses,
getEthSweepGasInfo,
lookupRecipient,
assertSupportedAddress,
getBlockNumberUserRegistered,
} from '../utils/utils';
import { Umbra as UmbraContract, Erc20 as ERC20 } from '@umbra/contracts-core/typechain';
import { ERC20_ABI, ETH_ADDRESS, UMBRA_ABI, UMBRA_BATCH_SEND_ABI } from '../utils/constants';
import type { Announcement, ChainConfig, EthersProvider, GraphFilterOverride, ScanOverrides, SendOverrides, SubgraphAnnouncement, UserAnnouncement, AnnouncementDetail, SendBatch, SendData} from '../types'; // prettier-ignore
Expand Down Expand Up @@ -364,8 +370,8 @@ export class Umbra {
}

/**
* @notice Fetches all Umbra event logs using The Graph, if available, falling back to RPC if not
* @param overrides Override the start and end block used for scanning; ignored if using The Graph
* @notice Fetches all Umbra event logs using Goldsky, if available, falling back to RPC if not
* @param overrides Override the start and end block used for scanning;
* @returns A list of Announcement events supplemented with additional metadata, such as the sender, block,
* timestamp, and txhash
*/
Expand All @@ -374,8 +380,8 @@ export class Umbra {
const startBlock = overrides.startBlock || this.chainConfig.startBlock;
const endBlock = overrides.endBlock || 'latest';

// Try querying events using the Graph, fallback to querying logs.
// The Graph fetching uses the browser's `fetch` method to query the subgraph, so we check
// Try querying events using Goldsky, fallback to querying logs.
// Goldsky fetching uses the browser's `fetch` method to query the subgraph, so we check
// that window is defined first to avoid trying to use fetch in node environments
if (typeof window !== 'undefined' && this.chainConfig.subgraphUrl) {
try {
Expand All @@ -391,11 +397,29 @@ export class Umbra {
}

/**
* @notice Fetches all Umbra event logs using The Graph
* @dev Currently ignores the start and end block parameters and returns all events; this may change in a
* future version
* @param startBlock Ignored
* @param endBlock Ignored
* @notice Fetches Umbra event logs starting from the block user registered their stealth keys in using
* Goldsky, if available, falling back to RPC if not
* @param overrides Override the start and end block used for scanning;
* @returns A list of Announcement events supplemented with additional metadata, such as the sender, block,
* timestamp, and txhash
*/
async fetchSomeAnnouncements(
Signer: JsonRpcSigner,
address: string,
overrides: ScanOverrides = {}
): Promise<AnnouncementDetail[]> {
const registeredBlockNumber = await getBlockNumberUserRegistered(address, Signer.provider);
// Get start and end blocks to scan events for
const startBlock = overrides.startBlock || registeredBlockNumber;
if (!startBlock) return [];
const endBlock = overrides.endBlock || 'latest';
return this.fetchAllAnnouncements({ startBlock, endBlock });
}

/**
* @notice Fetches all Umbra event logs using Goldsky
* @param startBlock Scanning start block
* @param endBlock Scannding end block
* @returns A list of Announcement events supplemented with additional metadata, such as the sender, block,
* timestamp, txhash, and the subgraph identifier
*/
Expand Down
9 changes: 9 additions & 0 deletions umbra-js/src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,15 @@ export async function lookupRecipient(
return { spendingPublicKey: publicKey, viewingPublicKey: publicKey };
}

export async function getBlockNumberUserRegistered(address: string, provider: StaticJsonRpcProvider) {
address = getAddress(address); // address input validation
const registry = new StealthKeyRegistry(provider);
const filter = registry._registry.filters.StealthKeyChanged(address, null, null, null, null);
const stealthKeyLogs = await registry._registry.queryFilter(filter);
const registryBlock = stealthKeyLogs[0]?.blockNumber || undefined;
return registryBlock;
}

/**
* @notice Throws if provided public key is not on the secp256k1 curve
* @param point Uncompressed public key as hex string
Expand Down