Skip to content

Commit

Permalink
feat(utils): add the serializeScript method in the utils module
Browse files Browse the repository at this point in the history
  • Loading branch information
Keith-CY committed Aug 29, 2019
1 parent a812ae3 commit 85dffcb
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 0 deletions.
68 changes: 68 additions & 0 deletions packages/ckb-sdk-utils/__tests__/serialization/fixtures.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"serializeArg": [
{
"arg": "",
"expected": "00000000"
},
{
"arg": "0x",
"expected": "00000000"
},
{
"arg": "0x00",
"expected": "0100000000"
},
{
"arg": "00",
"expected": "0100000000"
},
{
"arg": "0x1010101010101010101010",
"expected": "0b0000001010101010101010101010"
}
],
"serializeArgs": [
{
"args": [],
"expected": "04000000"
},
{
"args": ["0x10", "0x01"],
"expected": "160000000c0000001100000001000000100100000001"
},
{
"args": ["0x3954acece65096bfa81258983ddb83915fc56bd8"],
"expected": "2000000008000000140000003954acece65096bfa81258983ddb83915fc56bd8"
}
],
"serializeCodeHash": [
{
"codeHash": "0x68d5438ac952d2f584abf879527946a537e82c7f3c1cbf6d8ebf9767437d8e88",
"expected": "68d5438ac952d2f584abf879527946a537e82c7f3c1cbf6d8ebf9767437d8e88"
},
{
"codeHash": "68d5438ac952d2f584abf879527946a537e82c7f3c1cbf6d8ebf9767437d8e88",
"expected": "68d5438ac952d2f584abf879527946a537e82c7f3c1cbf6d8ebf9767437d8e88"
}
],
"serializeHashType": [
{
"hashType": "data",
"expected": "00"
},
{
"hashType": "type",
"expected": "01"
}
],
"serializeScript": [
{
"script": {
"codeHash": "0x68d5438ac952d2f584abf879527946a537e82c7f3c1cbf6d8ebf9767437d8e88",
"args": ["0x3954acece65096bfa81258983ddb83915fc56bd8"],
"hashType": "type"
},
"expected": "5100000010000000300000003100000068d5438ac952d2f584abf879527946a537e82c7f3c1cbf6d8ebf9767437d8e88012000000008000000140000003954acece65096bfa81258983ddb83915fc56bd8"
}
]
}
40 changes: 40 additions & 0 deletions packages/ckb-sdk-utils/__tests__/serialization/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const ckbUtils = require('../../lib')
const fixtures = require('./fixtures.json')

const { serializeHash, serializeArg, serializeArgs, serializeCodeHash, serializeHashType, serializeScript } = ckbUtils

describe('Serialize Arg and Hash', () => {
const fixtureTable = fixtures.serializeArg.map(({ arg, expected }) => [arg, expected])
test.each(fixtureTable)('%j => %s', (arg, expected) => {
expect(serializeArg(arg)).toBe(expected)
expect(serializeHash(arg)).toBe(expected)
})
})

describe('Serialize Args', () => {
const fixtureTable = fixtures.serializeArgs.map(({ args, expected }) => [args, expected])
test.each(fixtureTable)('%j => %s', (args, expected) => {
expect(serializeArgs(args)).toBe(expected)
})
})

describe('Serialize CodeHash', () => {
const fixtureTable = fixtures.serializeCodeHash.map(({ codeHash, expected }) => [codeHash, expected])
test.each(fixtureTable)('%s => %s', (codeHash, expected) => {
expect(serializeCodeHash(codeHash)).toBe(expected)
})
})

describe('Serialize HashType', () => {
const fixtureTable = fixtures.serializeHashType.map(({ hashType, expected }) => [hashType, expected])
test.each(fixtureTable)('%s => %s', (hashType, expected) => {
expect(serializeHashType(hashType)).toBe(expected)
})
})

describe('Serialize Script', () => {
const fixtureTable = fixtures.serializeScript.map(({ script, expected }) => [script, expected])
test.each(fixtureTable)('%j => %s', (script, expected) => {
expect(serializeScript(script)).toBe(expected)
})
})
7 changes: 7 additions & 0 deletions packages/ckb-sdk-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as util from 'util'
import crypto from './crypto'

export * from './address'
export * from './serialization'

declare const TextDecoder: any // will be removed when Node@11 becomes LTS
declare const TextEncoder: any // will be removed when Node@11 becomes LTS
Expand Down Expand Up @@ -61,3 +62,9 @@ export const scriptToHash = ({ codeHash = '', args = [], hashType = 'data' }: CK
const digest = s.digest('hex')
return digest as string
}

export const toHexInLittleEndian = (int: number, paddingBytes: number = 4) =>
(+int)
.toString(16)
.replace(/^(.(..)*)$/, '0$1')
.padEnd(paddingBytes * 2, '0')
71 changes: 71 additions & 0 deletions packages/ckb-sdk-utils/src/serialization/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { hexToBytes, bytesToHex, toHexInLittleEndian } from '..'

const offsetSize = 4 // 4 bytes
const fullLengthSize = 4 // 4 bytes

const getOffsets = (elmLengths: number[]) => {
const headerLength = fullLengthSize + offsetSize * elmLengths.length
const offsets = [headerLength]
elmLengths.forEach((_, idx: number) => {
if (idx) {
offsets.push(offsets[offsets.length - 1] + elmLengths[idx - 1])
}
})
return offsets
}

export const serializeHash = (arg: CKBComponents.Hash) => {
const bytes = hexToBytes(arg)
const header = toHexInLittleEndian(bytes.length)
const body = bytesToHex(bytes)
return `${header}${body}`
}

export const serializeCodeHash = (codeHash: CKBComponents.Hash256) => {
const bytes = hexToBytes(codeHash)
const body = bytesToHex(bytes)
return body
}

export const serializeArg = (arg: CKBComponents.Hash) => serializeHash(arg)

export const serializeHashType = (hashType: CKBComponents.ScriptHashType) => {
if (hashType === 'data') return '00'
if (hashType === 'type') return '01'
throw new TypeError("Hash type must by either of 'data' or 'type'")
}

export const serializeArgs = (args: string[]) => {
if (!args.length) return '04000000'
const serializedArgs = args.map(serializeArg)
const headerLength = fullLengthSize + offsetSize * serializedArgs.length
const fullLength = toHexInLittleEndian(headerLength + serializedArgs.join('').length / 2)
const offsets = getOffsets(serializedArgs.map(arg => arg.length / 2)).map(offset => toHexInLittleEndian(offset))
return [fullLength, ...offsets, ...serializedArgs].join('')
}

export const serializeScript = (script: CKBComponents.Script) => {
if (!script) throw new Error('Script is required')
const { codeHash = '', hashType, args = [] } = script
const serializedCodeHash = serializeCodeHash(codeHash)
const serializedHashType = serializeHashType(hashType)
const serializedArgs = serializeArgs(args)
const elms = [serializedCodeHash, serializedHashType, serializedArgs]
const fullLength = toHexInLittleEndian(
fullLengthSize +
offsetSize * 3 +
(serializedCodeHash.length + serializedHashType.length + serializedArgs.length) / 2
)

const offsets = getOffsets(elms.map(elm => elm.length / 2)).map(offset => toHexInLittleEndian(offset))
return [fullLength, ...offsets, ...elms].join('')
}

export default {
serializeHash,
serializeCodeHash,
serializeArg,
serializeArgs,
serializeHashType,
serializeScript,
}

0 comments on commit 85dffcb

Please sign in to comment.