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

feat(orchestration): lookup ChainInfo from address value #10354

Merged
merged 4 commits into from
Nov 1, 2024

Conversation

0xpatrickdev
Copy link
Member

@0xpatrickdev 0xpatrickdev commented Oct 28, 2024

closes: #10349

Description

  • add bech32Prefix?: string to CosmosChainInfo and populate data with yarn codegen
  • add getChainInfoByAddress lookup method to ChainHub

Security Considerations

If multiple cosmos chains use the same bech32Prefix, this approach is not sound. In practice, this doesn't seem to be an issue. Developers also control their own ChainHubs and choose whether they want to populate it with data from agoricNames. The potential impact is loss of funds from a misrouted transfer.

Scaling Considerations

  • Adds another exo to keep track of bech32Prefix:chainName mapping.
  • Adds more data to agoricNames (negligible)

Documentation Considerations

Typedoc will include these changes

Testing Considerations

Includes unit tests and updates snapshot tests

Upgrade Considerations

Library code, part of an NPM orch release.

@0xpatrickdev 0xpatrickdev requested a review from a team as a code owner October 28, 2024 22:35
Copy link

cloudflare-workers-and-pages bot commented Oct 28, 2024

Deploying agoric-sdk with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8ba4699
Status: ✅  Deploy successful!
Preview URL: https://e1c8bb50.agoric-sdk.pages.dev
Branch Preview URL: https://pc-bech-32-prefix.agoric-sdk.pages.dev

View logs

@0xpatrickdev 0xpatrickdev force-pushed the pc/bech-32-prefix branch 2 times, most recently from cf708f8 to 832d26f Compare October 29, 2024 00:37
@0xpatrickdev 0xpatrickdev force-pushed the pc/bech-32-prefix branch 2 times, most recently from 4ad588d to 33b5f80 Compare October 29, 2024 14:36
Copy link
Member

@dckc dckc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking, lastIndexOf is a correctness issue. I suppose it deserves a test.

factoring out getBech32Prefix(addr) is a suggestion; deal with it at your discretion.

@@ -216,6 +217,11 @@ export const makeChainHub = (zone, agoricNames, vowTools) => {
keyShape: BrandShape,
valueShape: M.string(),
});
/** @type {MapStore<string, string>} */
const bech32PrefixToChainName = zone.mapStore('bech32PrefixToChainName', {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 1st arg to makeScalarMapStore is a tag for just the key, not the whole map, for use in error messages such as bech32Prefix not found: blort. So I was going to suggest:

Suggested change
const bech32PrefixToChainName = zone.mapStore('bech32PrefixToChainName', {
const bech32PrefixToChainName = zone.mapStore('bech32Prefix', {

but it looks like the arg here is a baggage key, so the longer name seems right.

Comment on lines 445 to 447
const [prefix, rest] = address.split('1');
(prefix && rest) ||
Fail`Address contains invalid bech32 separator: ${q(address)}`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cosmjs uses bech32, which uses lastIndexOf, not the 1st occurrence.

https://github.com/bitcoinjs/bech32/blob/5ceb0e3d4625561a459c85643ca6947739b2d83c/src/index.ts#L146

I wonder about factoring out a getBech32Prefix(addr) with its own unit tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - glad I tagged you as a reviewer! Will factor this out to its own function and adopt the new logic.

Copy link
Member Author

@0xpatrickdev 0xpatrickdev Oct 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, in practice no one seems to use 1 in the prefix / human readable part:
https://github.com/satoshilabs/slips/blob/master/slip-0173.md#registered-human-readable-parts

But the spec does say any US-ASCII character, in the range [33-126], is valid for the HRP:
https://github.com/bitcoin/bips/blob/7a104491862379e88662a39d1a2b613c907e503e/bip-0173.mediawiki#bech32

Please see the amended commit: 65e07f8

* @param {string} address bech32 address
* @returns {CosmosChainInfo}
*/
getChainInfoFromAddress(address) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you consider getChainInfoFromPrefix(getBech32Prefix(address)) instead or in addition?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't, no.

in addition

I think we should only have one function for this to reduce info overload.

instead

I still feel getChainInfoFromAddress is the sensible abstraction, but happy to refactor to getChainInfoFromPrefix if you and other reviewers are in agreement that its preferable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This starts to look like a product question. getChainInfoFromAddress is what #10349 calls for, and I guess it got an OK from product.

I don't feel strongly about it. I guess getChainInfoFromAddress is fine by me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For transparency, I wrote the ticket and prescribed the name there. I don't think product typically weighs in on function names, just the user story (here: "look up chainId from chain address value").

@0xpatrickdev 0xpatrickdev force-pushed the pc/bech-32-prefix branch 2 times, most recently from c832f82 to 28bbace Compare October 30, 2024 15:14
@0xpatrickdev 0xpatrickdev requested a review from dckc October 30, 2024 15:16
Comment on lines 126 to 127
specimen: '111qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4',
expected: '11',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

This test distinguishes the correct .lastIndexOf() implementation from the indexOf() style.

@0xpatrickdev 0xpatrickdev added the automerge:rebase Automatically rebase updates, then merge label Oct 30, 2024
@0xpatrickdev 0xpatrickdev removed the automerge:rebase Automatically rebase updates, then merge label Oct 30, 2024
@0xpatrickdev 0xpatrickdev force-pushed the pc/bech-32-prefix branch 2 times, most recently from 565ad9a to c94f5fb Compare October 30, 2024 17:48
Comment on lines 447 to 451
if (bech32PrefixToChainName.has(prefix)) {
const chainName = bech32PrefixToChainName.get(prefix);
return chainInfos.get(chainName);
}
throw makeError(`Chain info not found for bech32Prefix ${q(prefix)}`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider: generally better to validate before getting to the happy path:

Suggested change
if (bech32PrefixToChainName.has(prefix)) {
const chainName = bech32PrefixToChainName.get(prefix);
return chainInfos.get(chainName);
}
throw makeError(`Chain info not found for bech32Prefix ${q(prefix)}`);
if (!bech32PrefixToChainName.has(prefix)) {
throw makeError(`Chain info not found for bech32Prefix ${q(prefix)}`);
}
const chainName = bech32PrefixToChainName.get(prefix);
return chainInfos.get(chainName);

@@ -177,3 +159,35 @@ test('toward asset info in agoricNames (#9572)', async t => {
});
}
});

test.serial('getChainInfoByAddress', async t => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this serial now? each test has its own setup.

we should avoid serial when we can

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this, and the addition of the getChainInfoByAddress test, toward asset info in agoricNames (#9572) starts failing with:

SES_UNHANDLED_REJECTION: (Error#1)
Error#1: promise watcher must be a virtual object
    at makeError (file:///Users/0xpatrick/repos/agoric-sdk/node_modules/ses/src/error/assert.js:350:61)
    at fail (file:///Users/0xpatrick/repos/agoric-sdk/node_modules/ses/src/error/assert.js:482:20)
    at Fail (file:///Users/0xpatrick/repos/agoric-sdk/node_modules/ses/src/error/assert.js:492:39)
    at file:///Users/0xpatrick/repos/agoric-sdk/packages/swingset-liveslots/src/watchedPromises.js:216:13
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

  ─

  toward asset info in agoricNames (#9572)

  Error: Promise returned by test never resolved

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, out of scope. Turns out provideDurableZone mutates global state. I'll look into it

},
];

for (const { specimen, expected, error } of testCases) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider an ava macro (e.g. bech32) so you can have a test for each case:

test(bech32, 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', 'bc');
…
test(bech32, '1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', null, 'Missing prefix for "1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"');

@0xpatrickdev 0xpatrickdev force-pushed the pc/bech-32-prefix branch 3 times, most recently from 6c9e11d to 08ed142 Compare October 30, 2024 18:57
@0xpatrickdev 0xpatrickdev added the automerge:rebase Automatically rebase updates, then merge label Oct 31, 2024
- takes a ChainAddress.value, parses the bech32Prefix, and looks up the corresponding ChainInfo
- prevents empty entries from affecting sort order and causing unnecessary git diff churn
@mergify mergify bot merged commit aba3e48 into master Nov 1, 2024
80 checks passed
@mergify mergify bot deleted the pc/bech-32-prefix branch November 1, 2024 00:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge:rebase Automatically rebase updates, then merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

orchestration: look up chainId from chain address value
4 participants