-
Notifications
You must be signed in to change notification settings - Fork 230
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
Conversation
Deploying agoric-sdk with
|
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 |
5e83e42
to
bbcc8d7
Compare
cf708f8
to
832d26f
Compare
4ad588d
to
33b5f80
Compare
There was a problem hiding this 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', { |
There was a problem hiding this comment.
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:
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.
const [prefix, rest] = address.split('1'); | ||
(prefix && rest) || | ||
Fail`Address contains invalid bech32 separator: ${q(address)}`; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the so-called reference implementation uses .rfind('1')
too.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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").
c832f82
to
28bbace
Compare
specimen: '111qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', | ||
expected: '11', |
There was a problem hiding this comment.
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.
28bbace
to
9613b10
Compare
565ad9a
to
c94f5fb
Compare
if (bech32PrefixToChainName.has(prefix)) { | ||
const chainName = bech32PrefixToChainName.get(prefix); | ||
return chainInfos.get(chainName); | ||
} | ||
throw makeError(`Chain info not found for bech32Prefix ${q(prefix)}`); |
There was a problem hiding this comment.
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:
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 => { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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"');
6c9e11d
to
08ed142
Compare
08ed142
to
9ec159c
Compare
- 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
9ec159c
to
8ba4699
Compare
closes: #10349
Description
bech32Prefix?: string
toCosmosChainInfo
and populate data withyarn codegen
getChainInfoByAddress
lookup method toChainHub
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 fromagoricNames
. The potential impact is loss of funds from a misrouted transfer.Scaling Considerations
bech32Prefix:chainName
mapping.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.