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

client/js: When using Hardhat, fetchRuntimePublicKey falls through to fetchRuntimePublicKeyByChainId #146

Closed
CedarMist opened this issue Jul 5, 2023 · 0 comments · Fixed by #153

Comments

@CedarMist
Copy link
Member

CedarMist commented Jul 5, 2023

There is a problem when using CI or a multi-container environment, for example Hardhat with the sapphire-dev where both are in containers on the same Docker network, where both containers have separate 172.16.x IP addresses.

The fetchRuntimePublicKey function isn't using the configured upstream JSON-RPC provider to request the call data public key. This means it's trying to access 127.0.0.1:8545 which won't work - because the sapphire-dev container is on another 172.x IP.

This is causing hardhat test to fail, but it works when running the tests locally, or when running the tests on the CI host rather than inside a container.

While this is annoying for CI, the more general problem is the provider detection code in fetchRuntimePublicKey doesn't seem to be functioning 100%.


One of the problems is there's duplicated logic to find the correct .send method inside fetchRuntimePublicKey because it's being called prior to wrapping the provider.

If it's moved until after the provider has been wrapped, then it can use the consistent EIP-1193 .request method.

export async function fetchRuntimePublicKey(

is called from

function getCipher(provider: UpstreamProvider): Cipher {

which is called from the top of the wrap function at:

const provider = new JsonRpcProvider(upstream);

and

const cipher = customCipher ?? getCipher(upstream);


Some notes on .send, if the provider isn't EIP-1193 compatible which has a standardized .request method. It will fall back to having to use .send which is wildly inconsistent across implementations.

  • Ethers (v5 & v6) JSON-RPC provider = send(method:string,params[]) -> Promise
  • Truffle = send(payload, callback) -> void

With .request it can be simplified as:

+    // EIP-1193 provider
+    if( 'request' in provider ) {
+      const {key} = await (provider as EIP1193Provider).request({method: OASIS_CALL_DATA_PUBLIC_KEY}) as {key:string};
+      if( key ) return arrayify(key);
+    }

Truffle HDWallet-provider: https://github.com/trufflesuite/truffle/blob/a6fb238ef954d3ad4c6fd4181b78ee3dc047c8bf/packages/hdwallet-provider/src/index.ts#L364-L368

public send(
    payload: JSONRPCRequestPayload,
    // @ts-ignore we patch this method so it doesn't conform to type
    callback: (error: null | Error, response: JSONRPCResponsePayload) => void
  ): void

EIP-1193: https://eips.ethereum.org/EIPS/eip-1193#appendix-i-consumer-facing-api-documentation

interface RequestArguments {
  readonly method: string;
  readonly params?: readonly unknown[] | object;
}

Provider.request(args: RequestArguments): Promise<unknown>;

Ethers JSON-RPC provider: https://github.com/ethers-io/ethers.js/blob/9197f9f938b5f3b5f97c043f2dab06854656c932/src.ts/providers/provider-jsonrpc.ts#L1139

async send(method: string, params: Array<any> | Record<string, any>): Promise<any> {

Web3 provider .send: https://docs.web3js.org/api/web3-providers-http/class/HttpProvider#send which notes the interface is deprecated and to use .request instead.

The big problem seems to be that neither Truffle nor Ethers support .request...

The isEthersSend function tries to determine between them:

function isEthersSend(
send?: AsyncSend | JsonRpcProvider['send'],
): send is JsonRpcProvider['send'] {
if (!send) return false;
// If the function is async, it's likely ethers send.
try {
const res = (send as any)(); // either rejects or calls back with an error
if (res instanceof Promise) {
res.catch(() => void {}); // handle the rejection before the next tick
return true;
}
} catch {
// This is prophyalictic. Neither kind of `send` should synchronously throw.
}
return false;
}

And I used a bad hack to detect Truffle HDWallet provider:

const arg =
'engine' in provider
? // eslint-disable-next-line @typescript-eslint/no-unused-vars
function (err: any, ok?: any) {
return;
}
: [];
const { key } = await source.send(OASIS_CALL_DATA_PUBLIC_KEY, arg);

But this needs cleaning up to make sure it's compatible with:

  • Truffle
  • Hardhat
  • Metamask
  • EIP-1193
  • Ethers
@CedarMist CedarMist changed the title When using Hardhat, fetchRuntimePublicKey falls through to fetchRuntimePublicKeyByChainId client/js: When using Hardhat, fetchRuntimePublicKey falls through to fetchRuntimePublicKeyByChainId Jul 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant