Skip to content

Commit

Permalink
bech32 package (#979)
Browse files Browse the repository at this point in the history
  • Loading branch information
turbocrime authored Apr 26, 2024
1 parent b8c8260 commit fdd4303
Show file tree
Hide file tree
Showing 108 changed files with 1,269 additions and 710 deletions.
5 changes: 5 additions & 0 deletions .changeset/slow-bulldogs-collect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@penumbra-zone/bech32m': major
---

new package to replace `@penumbra-zone/bech32`. validation is much more complete and correct
2 changes: 1 addition & 1 deletion apps/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"@connectrpc/connect": "^1.4.0",
"@connectrpc/connect-web": "^1.4.0",
"@penumbra-labs/registry": "^5.1.0",
"@penumbra-zone/bech32": "workspace:*",
"@penumbra-zone/bech32m": "workspace:*",
"@penumbra-zone/client": "workspace:*",
"@penumbra-zone/constants": "workspace:*",
"@penumbra-zone/crypto-web": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useStore } from '../../../state';
import { passwordSelector } from '../../../state/password';
import { KeyGradientIcon } from '../../../icons/key-gradient';
import { walletsSelector } from '../../../state/wallets';
import { bech32FullViewingKey } from '@penumbra-zone/bech32/src/full-viewing-key';
import { bech32mFullViewingKey } from '@penumbra-zone/bech32m/penumbrafullviewingkey';
import { SettingsScreen } from './settings-screen';

export const SettingsFullViewingKey = () => {
Expand All @@ -23,7 +23,7 @@ export const SettingsFullViewingKey = () => {

void (async function () {
if (await isPassword(password)) {
setFullViewingKey(bech32FullViewingKey(await getFullViewingKey()));
setFullViewingKey(bech32mFullViewingKey(await getFullViewingKey()));
} else {
setEnteredIncorrect(true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useStore } from '../../../state';
import { passwordSelector } from '../../../state/password';
import { AccountKeyGradientIcon } from '../../../icons/account-key-gradient';
import { walletsSelector } from '../../../state/wallets';
import { bech32SpendKey } from '@penumbra-zone/bech32/src/spend-key';
import { bech32mSpendKey } from '@penumbra-zone/bech32m/penumbraspendkey';
import { SettingsScreen } from './settings-screen';

export const SettingsSpendKey = () => {
Expand All @@ -23,7 +23,7 @@ export const SettingsSpendKey = () => {

void (async function () {
if (await isPassword(password)) {
setSpendKey(bech32SpendKey(await getSpendKey()));
setSpendKey(bech32mSpendKey(await getSpendKey()));
} else {
setEnteredIncorrect(true);
}
Expand Down
3 changes: 2 additions & 1 deletion apps/minifront/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"@cosmos-kit/react": "^2.11.0",
"@interchain-ui/react": "^1.23.3",
"@penumbra-labs/registry": "^5.1.0",
"@penumbra-zone/bech32": "workspace:*",
"@penumbra-zone/bech32m": "workspace:*",
"@penumbra-zone/client": "workspace:*",
"@penumbra-zone/constants": "workspace:*",
"@penumbra-zone/crypto-web": "workspace:*",
Expand All @@ -34,6 +34,7 @@
"@penumbra-zone/ui": "workspace:*",
"@radix-ui/react-icons": "^1.3.0",
"@tanstack/react-query": "^5.28.9",
"bech32": "^2.0.0",
"bignumber.js": "^9.1.2",
"chain-registry": "^1.41.9",
"cosmos-kit": "^2.10.0",
Expand Down
4 changes: 2 additions & 2 deletions apps/minifront/src/components/send/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { isAddress } from '@penumbra-zone/bech32m/penumbra';
import { Validation } from '../shared/validation-result';
import { isPenumbraAddr } from '@penumbra-zone/bech32/src/address';

export const penumbraAddrValidation = (): Validation => {
return {
type: 'error',
issue: 'invalid address',
checkFn: (addr: string) => Boolean(addr) && !isPenumbraAddr(addr),
checkFn: (addr: string) => Boolean(addr) && !isAddress(addr),
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,24 @@ import {
} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb';
import { STAKING_TOKEN_METADATA } from '@penumbra-zone/constants/src/assets';
import { ValidatorInfo } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/component/stake/v1/stake_pb';
import { bech32mIdentityKey } from '@penumbra-zone/bech32m/penumbravalid';

const u8 = (length: number) => Uint8Array.from({ length }, () => Math.floor(Math.random() * 256));

const validatorIk = { ik: u8(32) };
const validatorIkString = bech32mIdentityKey(validatorIk);
const delString = 'delegation_' + validatorIkString;
const udelString = 'udelegation_' + validatorIkString;
const delAsset = { inner: u8(32) };

const otherAsset = { inner: u8(32) };

const DELEGATION_TOKEN_METADATA = new Metadata({
display: 'delegation_penumbravalid1abc123',
base: 'udelegation_penumbravalid1abc123',
denomUnits: [
{ denom: 'udelegation_penumbravalid1abc123' },
{ denom: 'delegation_penumbravalid1abc123', exponent: 6 },
],
display: delString,
base: udelString,
denomUnits: [{ denom: udelString }, { denom: delString, exponent: 6 }],
name: 'Delegation token',
penumbraAssetId: { inner: new Uint8Array([0, 1, 2, 3]) },
penumbraAssetId: delAsset,
symbol: 'delUM(abc...xyz)',
});

Expand All @@ -25,7 +33,7 @@ const SOME_OTHER_TOKEN_METADATA = new Metadata({
base: 'usomeOtherToken',
denomUnits: [{ denom: 'usomeOtherToken' }, { denom: 'someOtherToken', exponent: 6 }],
name: 'Some Other Token',
penumbraAssetId: { inner: new Uint8Array([4, 5, 6, 7]) },
penumbraAssetId: otherAsset,
symbol: 'SOT',
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { Metadata } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/
import { AssetIcon } from '@penumbra-zone/ui/components/ui/tx/view/asset-icon';

/**
* Renders a single `ValidatorInfo`: its name, bech32-encoded identity key,
* Renders a single `ValidatorInfo`: its name and identity key,
* voting power, and commission.
*/
export const ValidatorInfoComponent = ({
Expand Down
9 changes: 5 additions & 4 deletions apps/minifront/src/components/staking/account/delegations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { AllSlices } from '../../../state';
import { DelegationValueView } from './delegation-value-view';
import { ValueView } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb';
import { useStoreShallow } from '../../../utils/use-store-shallow';
import { getIdentityKeyFromValueView } from '@penumbra-zone/getters/src/value-view';
import { bech32IdentityKey } from '@penumbra-zone/bech32/src/identity-key';
import { getValidatorIdentityKeyFromValueView } from '@penumbra-zone/getters/src/value-view';
import { bech32mIdentityKey } from '@penumbra-zone/bech32m/penumbravalid';
import { VotingPowerAsIntegerPercentage } from '@penumbra-zone/types/src/staking';

const getVotingPowerAsIntegerPercentage = (
votingPowerByValidatorInfo: Record<string, VotingPowerAsIntegerPercentage>,
delegation: ValueView,
) => votingPowerByValidatorInfo[bech32IdentityKey(getIdentityKeyFromValueView(delegation))];
) =>
votingPowerByValidatorInfo[bech32mIdentityKey(getValidatorIdentityKeyFromValueView(delegation))];

const delegationsSelector = (state: AllSlices) => ({
delegations: state.staking.delegationsByAccount.get(state.staking.account) ?? [],
Expand All @@ -27,7 +28,7 @@ export const Delegations = () => {
<AnimatePresence>
{delegations.map(delegation => (
<motion.div
key={bech32IdentityKey(getIdentityKeyFromValueView(delegation))}
key={bech32mIdentityKey(getValidatorIdentityKeyFromValueView(delegation))}
layout
className='bg-charcoal'
>
Expand Down
4 changes: 2 additions & 2 deletions apps/minifront/src/fetchers/address.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Address } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/keys/v1/keys_pb';
import { viewClient } from '../clients';
import { bech32Address } from '@penumbra-zone/bech32/src/address';
import { bech32mAddress } from '@penumbra-zone/bech32m/penumbra';

type Index = number;
type Bech32Address = string;
Expand All @@ -15,7 +15,7 @@ export const getAddresses = async (accounts: (number | undefined)[]): Promise<In
.map((address, i) => {
return {
index: accounts[i] ?? 0,
address: bech32Address(address),
address: bech32mAddress(address),
};
})
.reduce<IndexAddrRecord>((acc, curr) => {
Expand Down
10 changes: 4 additions & 6 deletions apps/minifront/src/state/ibc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { sendValidationErrors } from './send';
import { AddressView } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/keys/v1/keys_pb';
import { produce } from 'immer';
import { BalancesResponse } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb';
import { bech32ToAddress } from '@penumbra-zone/bech32/src/address';
import { addressFromBech32m } from '@penumbra-zone/bech32m/penumbra';
import { Chain } from '@penumbra-labs/registry';
import { currentTimePlusTwoDaysRounded } from './ibc';

Expand All @@ -32,11 +32,9 @@ describe.skip('IBC Slice', () => {
addressView: {
case: 'opaque',
value: {
address: {
inner: bech32ToAddress(
'penumbra1e8k5cyds484dxvapeamwveh5khqv4jsvyvaf5wwxaaccgfghm229qw03pcar3ryy8smptevstycch0qk3uu0rgkvtjpxy3cu3rjd0agawqtlz6erev28a6sg69u7cxy0t02nd4',
),
},
address: addressFromBech32m(
'penumbra1e8k5cyds484dxvapeamwveh5khqv4jsvyvaf5wwxaaccgfghm229qw03pcar3ryy8smptevstycch0qk3uu0rgkvtjpxy3cu3rjd0agawqtlz6erev28a6sg69u7cxy0t02nd4',
),
},
},
}),
Expand Down
15 changes: 11 additions & 4 deletions apps/minifront/src/state/ibc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
localAssets,
STAKING_TOKEN_METADATA,
} from '@penumbra-zone/constants/src/assets';
import { bech32IsValid } from '@penumbra-zone/bech32/src/validate';
import { bech32, bech32m } from 'bech32';
import { errorToast } from '@penumbra-zone/ui/lib/toast/presets';
import { Chain } from '@penumbra-labs/registry';

Expand Down Expand Up @@ -193,16 +193,23 @@ export const ibcValidationErrors = (state: AllSlices) => {
return {
recipientErr: !state.ibc.destinationChainAddress
? false
: !validateAddress(state.ibc.chain, state.ibc.destinationChainAddress),
: !validateUnknownAddress(state.ibc.chain, state.ibc.destinationChainAddress),
amountErr: !state.ibc.selection
? false
: amountMoreThanBalance(state.ibc.selection, state.ibc.amount),
};
};

const validateAddress = (chain: Chain | undefined, address: string): boolean => {
/**
* we don't know what format foreign addresses are in. so this only checks:
* - it's valid bech32 OR valid bech32m
* - the prefix matches the chain
*/
const validateUnknownAddress = (chain: Chain | undefined, address: string): boolean => {
if (!chain || address === '') return false;
return bech32IsValid(address, chain.addressPrefix);
const { prefix, words } =
bech32.decodeUnsafe(address, Infinity) ?? bech32m.decodeUnsafe(address, Infinity) ?? {};
return !words || prefix !== chain.addressPrefix;
};

/**
Expand Down
15 changes: 6 additions & 9 deletions apps/minifront/src/state/send.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import {
TransactionPlannerResponse,
} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb';
import { Fee } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/component/fee/v1/fee_pb';
import { stringToUint8Array } from '@penumbra-zone/types/src/string';
import { bech32ToAddress } from '@penumbra-zone/bech32/src/address';
import { addressFromBech32m } from '@penumbra-zone/bech32m/penumbra';

vi.mock('../fetchers/address', () => ({
getAddressByIndex: vi.fn(),
Expand All @@ -35,7 +34,7 @@ describe('Send Slice', () => {
metadata: new Metadata({
display: 'xyz',
denomUnits: [{ denom: 'xyz', exponent: 6 }],
penumbraAssetId: { inner: stringToUint8Array('passet1239049023') },
penumbraAssetId: { inner: new Uint8Array(32) },
}),
},
},
Expand All @@ -44,11 +43,9 @@ describe('Send Slice', () => {
addressView: {
case: 'decoded',
value: {
address: {
inner: bech32ToAddress(
'penumbra1e8k5cyds484dxvapeamwveh5khqv4jsvyvaf5wwxaaccgfghm229qw03pcar3ryy8smptevstycch0qk3uu0rgkvtjpxy3cu3rjd0agawqtlz6erev28a6sg69u7cxy0t02nd4',
),
},
address: addressFromBech32m(
'penumbra1e8k5cyds484dxvapeamwveh5khqv4jsvyvaf5wwxaaccgfghm229qw03pcar3ryy8smptevstycch0qk3uu0rgkvtjpxy3cu3rjd0agawqtlz6erev28a6sg69u7cxy0t02nd4',
),
index: { account: 12 },
},
},
Expand Down Expand Up @@ -122,7 +119,7 @@ describe('Send Slice', () => {

describe('setRecipient and validate', () => {
const rightAddress =
'penumbra1lsqlh43cxh6amvtu0g84v9s8sq0zef4mz8jvje9lxwarancqg9qjf6nthhnjzlwngplepq7vaam8h4z530gys7x2s82zn0sgvxneea442q63sumem7r096p7rd2tywm2v6ppc4';
'penumbra1ftmn2a3hf8pxe0e48es8u9rqhny4xggq9wn2caxcjnfwfhwr5s0t3y6nzs9gx3ty5czd0sd9ssfgjt2pcxrq93yvgk2gu3ynmayuwgddkxthce8l445v8x6v07y2sjd8djcr6v';

test('recipient can be set and validate', () => {
useStore.getState().send.setSelection(selectionExample);
Expand Down
4 changes: 2 additions & 2 deletions apps/minifront/src/state/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import { getAddress, getAddressIndex } from '@penumbra-zone/getters/src/address-view';
import { toBaseUnit } from '@penumbra-zone/types/src/lo-hi';
import { fromValueView } from '@penumbra-zone/types/src/amount';
import { isPenumbraAddr } from '@penumbra-zone/bech32/src/address';
import { isAddress } from '@penumbra-zone/bech32m/penumbra';

export interface SendSlice {
selection: BalancesResponse | undefined;
Expand Down Expand Up @@ -172,7 +172,7 @@ export const sendValidationErrors = (
memo?: string,
): SendValidationFields => {
return {
recipientErr: Boolean(recipient) && !isPenumbraAddr(recipient),
recipientErr: Boolean(recipient) && !isAddress(recipient),
amountErr: !asset ? false : amountMoreThanBalance(asset, amount),
// The memo cannot exceed 512 bytes
// return address uses 80 bytes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,14 @@ import {
import { sctClient, stakeClient, viewClient } from '../../clients';
import {
getAmount,
getValidatorIdentityKeyFromValueView,
getUnbondingStartHeightFromValueView,
getValidatorIdentityKeyAsBech32StringFromValueView,
} from '@penumbra-zone/getters/src/value-view';
import { asIdentityKey } from '@penumbra-zone/getters/src/string';

const getUndelegateClaimPlannerRequest =
(endEpochIndex: bigint) => async (unbondingToken: ValueView) => {
const unbondingStartHeight = getUnbondingStartHeightFromValueView(unbondingToken);
const validatorIdentityKeyAsBech32String =
getValidatorIdentityKeyAsBech32StringFromValueView(unbondingToken);
const identityKey = asIdentityKey(validatorIdentityKeyAsBech32String);
const identityKey = getValidatorIdentityKeyFromValueView(unbondingToken);
const { epoch: startEpoch } = await sctClient.epochByHeight({ height: unbondingStartHeight });

const { penalty } = await stakeClient.validatorPenalty({
Expand Down
19 changes: 12 additions & 7 deletions apps/minifront/src/state/staking/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
Metadata,
ValueView,
} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb';
import { bech32IdentityKey } from '@penumbra-zone/bech32/src/identity-key';
import { bech32mIdentityKey } from '@penumbra-zone/bech32m/penumbravalid';
import { getValidatorInfoFromValueView } from '@penumbra-zone/getters/src/value-view';
import {
AddressView,
Expand All @@ -15,8 +15,9 @@ import {
import { THROTTLE_MS } from '.';
import { DelegationsByAddressIndexResponse } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb';

const validator1IdentityKey = new IdentityKey({ ik: new Uint8Array([1, 2, 3]) });
const validator1Bech32IdentityKey = bech32IdentityKey(validator1IdentityKey);
const u8 = (length: number) => Uint8Array.from({ length }, () => Math.floor(Math.random() * 256));
const validator1IdentityKey = new IdentityKey({ ik: u8(32) });
const validator1Bech32IdentityKey = bech32mIdentityKey(validator1IdentityKey);
const validatorInfo1 = new ValidatorInfo({
status: {
votingPower: { hi: 0n, lo: 2n },
Expand All @@ -27,8 +28,10 @@ const validatorInfo1 = new ValidatorInfo({
},
});

const validator2IdentityKey = new IdentityKey({ ik: new Uint8Array([4, 5, 6]) });
const validator2Bech32IdentityKey = bech32IdentityKey(validator2IdentityKey);
const validator2IdentityKey = new IdentityKey({
ik: u8(32),
});
const validator2Bech32IdentityKey = bech32mIdentityKey(validator2IdentityKey);
const validatorInfo2 = new ValidatorInfo({
status: {
votingPower: { hi: 0n, lo: 5n },
Expand All @@ -39,7 +42,9 @@ const validatorInfo2 = new ValidatorInfo({
},
});

const validator3IdentityKey = new IdentityKey({ ik: new Uint8Array([7, 8, 9]) });
const validator3IdentityKey = new IdentityKey({
ik: u8(32),
});
const validatorInfo3 = new ValidatorInfo({
status: {
votingPower: { hi: 0n, lo: 3n },
Expand All @@ -50,7 +55,7 @@ const validatorInfo3 = new ValidatorInfo({
},
});

const validator4IdentityKey = new IdentityKey({ ik: new Uint8Array([0]) });
const validator4IdentityKey = new IdentityKey({ ik: u8(32) });
const validatorInfo4 = new ValidatorInfo({
status: {
votingPower: { hi: 0n, lo: 9n },
Expand Down
10 changes: 4 additions & 6 deletions apps/minifront/src/state/swap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Amount } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/nu
import { localAssets } from '@penumbra-zone/constants/src/assets';
import { AddressView } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/keys/v1/keys_pb';
import { BalancesResponse } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb';
import { bech32ToAddress } from '@penumbra-zone/bech32/src/address';
import { addressFromBech32m } from '@penumbra-zone/bech32m/penumbra';

describe('Swap Slice', () => {
const selectionExample = new BalancesResponse({
Expand All @@ -29,11 +29,9 @@ describe('Swap Slice', () => {
addressView: {
case: 'opaque',
value: {
address: {
inner: bech32ToAddress(
'penumbra1e8k5cyds484dxvapeamwveh5khqv4jsvyvaf5wwxaaccgfghm229qw03pcar3ryy8smptevstycch0qk3uu0rgkvtjpxy3cu3rjd0agawqtlz6erev28a6sg69u7cxy0t02nd4',
),
},
address: addressFromBech32m(
'penumbra1e8k5cyds484dxvapeamwveh5khqv4jsvyvaf5wwxaaccgfghm229qw03pcar3ryy8smptevstycch0qk3uu0rgkvtjpxy3cu3rjd0agawqtlz6erev28a6sg69u7cxy0t02nd4',
),
},
},
}),
Expand Down
Loading

0 comments on commit fdd4303

Please sign in to comment.