-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(helpers): stricter encode/decode address (#347)
* refactor: stricter encode/decode address * test(helpers): cases for mixed bech32(m)
- Loading branch information
Showing
7 changed files
with
335 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md | ||
// | format type | description | | ||
// |:-----------:|--------------------------------------------------------------| | ||
// | 0x00 | full version identifies the hash_type | | ||
// | 0x01 | short version for locks with popular code_hash, deprecated | | ||
// | 0x02 | full version with hash_type = "Data", deprecated | | ||
// | 0x04 | full version with hash_type = "Type", deprecated | | ||
|
||
import { Address, Script } from "@ckb-lumos/base"; | ||
import { getConfig } from "@ckb-lumos/config-manager"; | ||
import { bech32, bech32m } from "bech32"; | ||
import { Options } from "./"; | ||
import { byteArrayToHex } from "./utils"; | ||
|
||
const BECH32_LIMIT = 1023; | ||
|
||
/** | ||
* full version identifies the hash_type | ||
*/ | ||
export const ADDRESS_FORMAT_FULL = 0x00; | ||
/** | ||
* @deprecated | ||
* short version for locks with popular code_hash, deprecated | ||
*/ | ||
export const ADDRESS_FORMAT_SHORT = 0x01; | ||
|
||
/** | ||
* @deprecated | ||
* full version with hash_type = "Data", deprecated | ||
*/ | ||
export const ADDRESS_FORMAT_FULLDATA = 0x02; | ||
|
||
/** | ||
* @deprecated | ||
* full version with hash_type = "Type", deprecated | ||
*/ | ||
export const ADDRESS_FORMAT_FULLTYPE = 0x04; | ||
|
||
export function parseFullFormatAddress( | ||
address: Address, | ||
{ config }: Options | ||
): Script { | ||
config = config || getConfig(); | ||
|
||
// throw error here if polymod not 0x2bc830a3(BECH32M_CONST) | ||
// https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki#bech32m | ||
const { words, prefix } = bech32m.decode(address, BECH32_LIMIT); | ||
|
||
if (prefix !== config.PREFIX) { | ||
throw Error( | ||
`Invalid prefix! Expected: ${config.PREFIX}, actual: ${prefix}` | ||
); | ||
} | ||
|
||
const [formatType, ...body] = bech32m.fromWords(words); | ||
|
||
if (formatType !== ADDRESS_FORMAT_FULL) { | ||
throw new Error("Invalid address format type"); | ||
} | ||
|
||
if (body.length < 32 + 1) { | ||
throw new Error("Invalid payload length, too short!"); | ||
} | ||
|
||
const code_hash = byteArrayToHex(body.slice(0, 32)); | ||
const hash_type = (() => { | ||
const serializedHashType = body[32]; | ||
|
||
if (serializedHashType === 0) return "data"; | ||
if (serializedHashType === 1) return "type"; | ||
if (serializedHashType === 2) return "data1"; | ||
|
||
throw new Error(`Invalid hash_type ${serializedHashType}`); | ||
})(); | ||
const args = byteArrayToHex(body.slice(33)); | ||
|
||
return { code_hash, hash_type, args }; | ||
} | ||
|
||
export function parseDeprecatedCkb2019Address( | ||
address: string, | ||
{ config }: Options | ||
): Script { | ||
config = config || getConfig(); | ||
|
||
// throw error here if polymod not 1 | ||
// https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki#bech32m | ||
const { prefix, words } = bech32.decode(address, BECH32_LIMIT); | ||
|
||
if (prefix !== config.PREFIX) { | ||
throw Error( | ||
`Invalid prefix! Expected: ${config.PREFIX}, actual: ${prefix}` | ||
); | ||
} | ||
const [formatType, ...body] = bech32.fromWords(words); | ||
|
||
switch (formatType) { | ||
// payload = 0x01 | code_hash_index | args | ||
case ADDRESS_FORMAT_SHORT: { | ||
const [shortId, ...argsBytes] = body; | ||
|
||
/* secp256k1 / multisig / ACP */ | ||
if (argsBytes.length !== 20) { | ||
throw Error(`Invalid payload length!`); | ||
} | ||
const scriptTemplate = Object.values(config.SCRIPTS).find( | ||
(s) => s && s.SHORT_ID === shortId | ||
); | ||
if (!scriptTemplate) { | ||
throw Error(`Invalid code hash index: ${shortId}!`); | ||
} | ||
return { | ||
code_hash: scriptTemplate.CODE_HASH, | ||
hash_type: scriptTemplate.HASH_TYPE, | ||
args: byteArrayToHex(argsBytes), | ||
}; | ||
} | ||
// payload = 0x02 | code_hash | args | ||
case ADDRESS_FORMAT_FULLDATA: { | ||
if (body.length < 32) { | ||
throw Error(`Invalid payload length!`); | ||
} | ||
return { | ||
code_hash: byteArrayToHex(body.slice(0, 32)), | ||
hash_type: "data", | ||
args: byteArrayToHex(body.slice(32)), | ||
}; | ||
} | ||
// payload = 0x04 | code_hash | args | ||
case ADDRESS_FORMAT_FULLTYPE: { | ||
if (body.length < 32) { | ||
throw Error(`Invalid payload length!`); | ||
} | ||
return { | ||
code_hash: byteArrayToHex(body.slice(0, 32)), | ||
hash_type: "type", | ||
args: byteArrayToHex(body.slice(32)), | ||
}; | ||
} | ||
} | ||
throw Error(`Invalid payload format type: ${formatType}`); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { HexString } from "@ckb-lumos/base"; | ||
|
||
export function hexToByteArray(h: HexString): number[] { | ||
if (!/^(0x)?([0-9a-fA-F][0-9a-fA-F])*$/.test(h)) { | ||
throw new Error("Invalid hex string!"); | ||
} | ||
if (h.startsWith("0x")) { | ||
h = h.slice(2); | ||
} | ||
const array = []; | ||
while (h.length >= 2) { | ||
array.push(parseInt(h.slice(0, 2), 16)); | ||
h = h.slice(2); | ||
} | ||
return array; | ||
} | ||
|
||
export function byteArrayToHex(a: number[]): HexString { | ||
return "0x" + a.map((i) => ("00" + i.toString(16)).slice(-2)).join(""); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
91a1d29
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.
Successfully deployed to the following URLs:
lumos-website – ./
lumos-website-git-develop-cryptape.vercel.app
lumos-website.vercel.app
lumos-website-cryptape.vercel.app