Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7f0e241ff |
@@ -1411,13 +1411,13 @@ that token's previous entry, as a failed ETH balance keeps its figure, or takes
|
||||
Blockscout's entry from the same refresh when there is none. Every other token
|
||||
comes from the Blockscout `token-balances` response (`fetchTokenBalances()`), so
|
||||
tokens do appear without the user adding them, and they keep their previous
|
||||
figures while Blockscout fails. Blockscout is asked only while the user has "Use
|
||||
Blockscout" on. While it is off, as it is until the user turns it on, a tracked
|
||||
token's entry has no holder count and no `decimals` of Blockscout's, so its
|
||||
balance is stated at the scale the bundled list or the tracked token records,
|
||||
and no other token is listed, those from earlier refreshes included. A tracked
|
||||
token is shown when its balance is nonzero, and any other ERC-20 when its
|
||||
balance is nonzero and it is in the bundled known-token list or has 1,000 or
|
||||
figures while Blockscout fails. The refresh asks Blockscout only while the user
|
||||
has "Use Blockscout" on. While it is off, as it is until the user turns it on, a
|
||||
tracked token's entry has no holder count and no `decimals` of Blockscout's, so
|
||||
its balance is stated at the scale the bundled list or the tracked token
|
||||
records, and no other token is listed, those from earlier refreshes included. A
|
||||
tracked token is shown when its balance is nonzero, and any other ERC-20 when
|
||||
its balance is nonzero and it is in the bundled known-token list or has 1,000 or
|
||||
more holders. A token claiming a symbol from the bundled list from any other
|
||||
contract address is always dropped, tracked or not, and so is any token claiming
|
||||
a symbol that belongs to the native asset and therefore has no legitimate
|
||||
@@ -2097,7 +2097,7 @@ view would leave a wallet one click from deletion.
|
||||
and raw data are read from that network's Blockscout, the one Settings has for
|
||||
it, and its etherscan links open that network's explorer. They are read only
|
||||
while "Use Blockscout" is on. The screen can still be shown with it off, when
|
||||
the popup reopens onto it after the user turned the setting off; then
|
||||
the user opens Settings from it, turns the setting off and presses Back; then
|
||||
Blockscout is not asked, and a line in their place says "No block, decoded
|
||||
details, network details or raw data are shown, because "Use Blockscout" is
|
||||
off in Settings."
|
||||
@@ -2752,9 +2752,12 @@ Contacted by default:
|
||||
them the balance of every tracked token at every address (`balanceOf`),
|
||||
estimate gas (`eth_estimateGas`), fetch nonces (`eth_getTransactionCount`),
|
||||
broadcast transactions (`eth_sendRawTransaction`), and check transaction
|
||||
receipts. The default endpoint is a public RPC (configurable by the user to
|
||||
any endpoint they prefer, including a local node). By default the extension
|
||||
talks to `https://ethereum-rpc.publicnode.com`.
|
||||
receipts. ENS lookups, of a name typed into Send and of the names shown for
|
||||
the user's addresses, are made through it alone, so a name whose resolver says
|
||||
it is resolved off-chain does not resolve. The default endpoint is a public
|
||||
RPC (configurable by the user to any endpoint they prefer, including a local
|
||||
node). By default the extension talks to
|
||||
`https://ethereum-rpc.publicnode.com`.
|
||||
- **Data sent**: Ethereum addresses, transaction data, contract call
|
||||
parameters. The RPC endpoint can see all on-chain queries and submitted
|
||||
transactions.
|
||||
@@ -2766,7 +2769,7 @@ Contacted by default:
|
||||
- **Data sent**: Token symbol strings only (e.g. "ETH", "USDC"). No
|
||||
addresses or user-specific data.
|
||||
|
||||
Contacted only while the user has turned the setting on:
|
||||
Sent the user's data only while the user has turned the setting on:
|
||||
|
||||
- **Blockscout block-explorer API** ("Use Blockscout"): Used to fetch
|
||||
transaction history (normal transactions and ERC-20 token transfers) for Home,
|
||||
|
||||
+6
-6
@@ -109,14 +109,14 @@ and calldata of a transaction you open. You can change this in Settings to a
|
||||
self-hosted Blockscout instance. While it is off, the screens that would show
|
||||
history say so, and only the tokens you track are listed.
|
||||
|
||||
When it is contacted: only while "Use Blockscout" is on, on every balance
|
||||
refresh, whenever a screen showing transaction history is opened, and when you
|
||||
open a transaction.
|
||||
When it is contacted: while "Use Blockscout" is on, on every balance refresh,
|
||||
whenever a screen showing transaction history is opened, and when you open a
|
||||
transaction. The one exception, whether or not the setting is on, is pressing
|
||||
"Save" beside the Blockscout URL in Settings, which checks that URL by asking it
|
||||
for `/stats` and sends none of your data.
|
||||
|
||||
What gets sent: your Ethereum addresses (to look up balances and transactions)
|
||||
and the hash of each transaction you open. Pressing "Save" beside the Blockscout
|
||||
URL in Settings checks that URL by asking it for `/stats`, which sends none of
|
||||
your data, whether or not the setting is on.
|
||||
and the hash of each transaction you open.
|
||||
|
||||
**CoinDesk CADLI price API** (`data-api.coindesk.com`)
|
||||
|
||||
|
||||
+10
-1
@@ -38,9 +38,18 @@ const { resolveTokenDecimals } = require("./approvalAmount");
|
||||
// ethers knows chains this wallet does not, so an id that is not one of ours
|
||||
// is a caller bug and must not resolve to a working provider for some other
|
||||
// chain.
|
||||
//
|
||||
// `disableCcipRead` keeps every ENS lookup on the RPC endpoint. Left on, as
|
||||
// ethers has it, a resolver that says a name is resolved off-chain makes ethers
|
||||
// fetch the web address the resolver gives, straight from the browser, with the
|
||||
// name being looked up; for the reverse lookup of the user's addresses that is
|
||||
// the user's own name (https://git.eeqj.de/sneak/AutistMask/issues/668). Such a
|
||||
// name does not resolve.
|
||||
function getProvider(rpcUrl, networkId) {
|
||||
const net = Network.from(requireNetworkId(networkId).id);
|
||||
return new JsonRpcProvider(rpcUrl, net, { staticNetwork: net });
|
||||
const provider = new JsonRpcProvider(rpcUrl, net, { staticNetwork: net });
|
||||
provider.disableCcipRead = true;
|
||||
return provider;
|
||||
}
|
||||
|
||||
function requireNetworkId(networkId) {
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
// Blockscout and Etherscan are asked nothing until the user turns them on
|
||||
// (https://git.eeqj.de/sneak/AutistMask/issues/668). A new profile has "Use
|
||||
// Blockscout" and "Check recipients on Etherscan" both off. Each request to
|
||||
// either service is skipped where it is made while its setting is off, and
|
||||
// made while it is on; the popup and the background hand the stored setting
|
||||
// to the code that makes the request.
|
||||
// Blockscout and Etherscan are sent none of the user's data until the user
|
||||
// turns them on (https://git.eeqj.de/sneak/AutistMask/issues/668). A new
|
||||
// profile has "Use Blockscout" and "Check recipients on Etherscan" both off.
|
||||
// Each request to either service is skipped where it is made while its setting
|
||||
// is off, and made while it is on; the popup and the background hand the
|
||||
// stored setting to the code that makes the request. The one exception is the
|
||||
// endpoint check Settings makes when the user presses "Save" beside the
|
||||
// Blockscout URL. It asks only for `/stats` and carries no user data; it is
|
||||
// made whether or not the setting is on.
|
||||
//
|
||||
// The functions that make the requests are driven directly, against a fetch()
|
||||
// that records every URL it is asked for. The screens are driven through the
|
||||
|
||||
+15
-11
@@ -269,18 +269,22 @@ test("the exact confirmation phrase erases the record and reloads into Welcome (
|
||||
}
|
||||
});
|
||||
|
||||
// ------------------ Blockscout and Etherscan are asked nothing (#668)
|
||||
// ------------------ Blockscout and Etherscan are sent no user data (#668)
|
||||
//
|
||||
// A fresh install asks neither Blockscout nor Etherscan anything until the
|
||||
// user turns on "Use Blockscout" or "Check recipients on Etherscan". The route
|
||||
// handler in tests/e2e/network.js records every request the extension makes,
|
||||
// from its pages and its background worker, in env.routeOpts.requests.
|
||||
// Recording starts when launch() in tests/e2e/harness.js installs that
|
||||
// handler, before the first popup page opens; a request the background worker
|
||||
// makes while it starts up, before then, is not recorded. These tests drive
|
||||
// every screen that would ask either service, and the background's own
|
||||
// balance refresh, before either setting is changed, then turn each on and
|
||||
// watch its requests start.
|
||||
// A fresh install sends Blockscout and Etherscan none of the user's data until
|
||||
// the user turns on "Use Blockscout" or "Check recipients on Etherscan". The
|
||||
// endpoint check Settings makes when the user presses "Save" beside the
|
||||
// Blockscout URL asks only for `/stats` and carries no user data; it is made
|
||||
// whether or not the setting is on. The route handler in tests/e2e/network.js
|
||||
// records every request the extension makes, from its pages and its background
|
||||
// worker, in env.routeOpts.requests. Recording starts when launch() in
|
||||
// tests/e2e/harness.js installs that handler, before the first popup page
|
||||
// opens; a request the background worker makes while it starts up, before
|
||||
// then, is not recorded. These tests drive Home, AddressDetail, AddressToken
|
||||
// and ConfirmTx, and the background's own balance refresh, before either
|
||||
// setting is changed, then turn each on and watch its requests start.
|
||||
// TransactionDetail, which also asks Blockscout, is left to the unit tests in
|
||||
// tests/blockscoutEtherscanSettings.test.js.
|
||||
|
||||
// What the three transaction lists say while Use Blockscout is off. Written
|
||||
// out here rather than imported, so the assertion does not compare the popup
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
// ENS lookups ask nothing but the RPC endpoint. A resolver can answer that a
|
||||
// name is resolved off-chain, naming a web address to fetch the answer from;
|
||||
// ethers fetches it unless the provider has `disableCcipRead` set, and that
|
||||
// request would carry the name to a server other than the RPC endpoint
|
||||
// (https://git.eeqj.de/sneak/AutistMask/issues/668). With it set, such a name
|
||||
// does not resolve.
|
||||
//
|
||||
// The provider getProvider() builds runs against a fake JSON-RPC node,
|
||||
// answered through ethers' own request hook, which also records the address of
|
||||
// every request ethers makes, the off-chain fetch included.
|
||||
|
||||
const { FetchRequest, Interface } = require("ethers");
|
||||
const { getProvider } = require("../src/shared/balances");
|
||||
|
||||
const RPC_URL = "https://rpc.example.invalid";
|
||||
const RESOLVER = "0x5555555555555555555555555555555555555555";
|
||||
const USER = "0x1111111111111111111111111111111111111111";
|
||||
const NAME = "offchain.eth";
|
||||
|
||||
// The ENS registry's resolver(), and the resolver's functions ethers calls: the
|
||||
// registry names RESOLVER for every name, the resolver says it resolves names
|
||||
// through resolve(), gives NAME as the reverse record of any address, and
|
||||
// answers resolve() with the OffchainLookup error that says the name is
|
||||
// resolved off-chain.
|
||||
const ens = new Interface([
|
||||
"function resolver(bytes32) view returns (address)",
|
||||
"function supportsInterface(bytes4) view returns (bool)",
|
||||
"function name(bytes32) view returns (string)",
|
||||
"function resolve(bytes, bytes) view returns (bytes)",
|
||||
"error OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData)",
|
||||
]);
|
||||
|
||||
let asked;
|
||||
|
||||
// One JSON-RPC answer. Anything but a call to one of the functions above is
|
||||
// refused.
|
||||
function reply(req) {
|
||||
const envelope = { jsonrpc: "2.0", id: req.id };
|
||||
const call =
|
||||
req.method === "eth_call" ? ens.parseTransaction(req.params[0]) : null;
|
||||
if (call === null) {
|
||||
return {
|
||||
...envelope,
|
||||
error: { code: -32601, message: "method not found" },
|
||||
};
|
||||
}
|
||||
if (call.name === "resolve") {
|
||||
const offchain = ens.encodeErrorResult("OffchainLookup", [
|
||||
RESOLVER,
|
||||
["https://gateway.example.invalid/{sender}/{data}.json"],
|
||||
"0x1234",
|
||||
"0x12345678",
|
||||
"0x",
|
||||
]);
|
||||
return {
|
||||
...envelope,
|
||||
error: { code: 3, message: "execution reverted", data: offchain },
|
||||
};
|
||||
}
|
||||
const answers = {
|
||||
resolver: [RESOLVER],
|
||||
supportsInterface: [true],
|
||||
name: [NAME],
|
||||
};
|
||||
return {
|
||||
...envelope,
|
||||
result: ens.encodeFunctionResult(call.name, answers[call.name]),
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
asked = [];
|
||||
FetchRequest.registerGetUrl(async (req) => {
|
||||
asked.push(req.url);
|
||||
// Any other address is the off-chain fetch, answered as not found.
|
||||
if (req.url !== RPC_URL) {
|
||||
return {
|
||||
statusCode: 404,
|
||||
statusMessage: "Not Found",
|
||||
headers: {},
|
||||
body: null,
|
||||
};
|
||||
}
|
||||
const body = JSON.parse(Buffer.from(req.body).toString("utf8"));
|
||||
const answer = Array.isArray(body) ? body.map(reply) : reply(body);
|
||||
return {
|
||||
statusCode: 200,
|
||||
statusMessage: "OK",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: Buffer.from(JSON.stringify(answer)),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
FetchRequest.registerGetUrl(FetchRequest.createGetUrlFunc());
|
||||
});
|
||||
|
||||
test("a name typed into Send that is resolved off-chain does not resolve, and only the RPC endpoint is asked", async () => {
|
||||
const provider = getProvider(RPC_URL, "mainnet");
|
||||
const [outcome] = await Promise.allSettled([provider.resolveName(NAME)]);
|
||||
expect(asked.filter((url) => url !== RPC_URL)).toEqual([]);
|
||||
expect(outcome).toEqual({ status: "fulfilled", value: null });
|
||||
});
|
||||
|
||||
test("the reverse lookup of a user's address whose name is resolved off-chain finds no name, and only the RPC endpoint is asked", async () => {
|
||||
const provider = getProvider(RPC_URL, "mainnet");
|
||||
const [outcome] = await Promise.allSettled([provider.lookupAddress(USER)]);
|
||||
expect(asked.filter((url) => url !== RPC_URL)).toEqual([]);
|
||||
expect(outcome).toEqual({ status: "fulfilled", value: null });
|
||||
});
|
||||
Reference in New Issue
Block a user