keystore_wdc
Version:
``` npm i keystore_wdc; const KeyStore = require('keystore_wdc'); const ks = new KeyStore(); ``` #### 生成keystore ``` async function create(){ const keystore = await ks.Create("your password"); } ``` * 返回keystore,密码格式不正确返回-1。
438 lines (387 loc) • 13.6 kB
text/typescript
import { ABI_DATA_TYPE, ABI_TYPE, AbiInput, Binary, MAX_U256, MAX_U64, ONE, Readable } from "./types"
import { OutputStream } from 'assemblyscript/cli/asc'
import {
assert,
bin2hex,
bin2str,
bytesToF64,
concatBytes,
convert,
hex2bin,
inverse,
normalizeAddress,
padPrefix,
publicKeyHash2Address,
rmd160,
str2bin,
toSafeInt
} from "./utils"
import BN = require("../bn")
import rlp = require('./rlp')
/**
* 合约部署的 paylod
*/
export function abiToBinary(abi: ABI[]): any[] {
const ret = []
for (let a of abi) {
ret.push([a.name, a.type === 'function' ? 0 : 1, a.inputs.map(x => ABI_DATA_TYPE[x.type]), a.outputs.map(x => ABI_DATA_TYPE[x.type])])
}
return ret
}
/**
* 计算合约地址
*/
export function getContractAddress(hash: Binary): string {
let buf = rlp.encode([hex2bin(hash), 0])
buf = rmd160(buf)
return publicKeyHash2Address(buf)
}
export async function compileContract(ascPath?: string, src?: string, opts?: { debug?: boolean, optimize?: boolean }): Promise<Uint8Array> {
if (typeof ascPath === 'string' && typeof src === 'string') {
const child_process = require('child_process')
let cmd = ascPath + ' ' + src + ' -b ' // 执行的命令
if (opts && opts.debug)
cmd += ' --debug '
if (opts && opts.optimize)
cmd += ' --optimize '
return new Promise((rs, rj) => {
child_process.exec(
cmd,
{ encoding: 'buffer' },
(err, stdout, stderr) => {
if (err) {
// err.code 是进程退出时的 exit code,非 0 都被认为错误
// err.signal 是结束进程时发送给它的信号值
rj(stderr.toString('ascii'))
return
}
rs(stdout)
}
)
})
}
const asc = require("assemblyscript/cli/asc")
if (typeof src !== 'string') {
src = ascPath
}
if (typeof src !== 'string')
throw new Error('invalid source file ' + src)
const arr = [
src,
"-b"
]
const stdout = new MemoryOutputStream()
const stderr = new MemoryOutputStream()
if (opts && opts.debug)
arr.push('--debug')
if (opts && opts.optimize)
arr.push('--optimize')
await asc.ready
return new Promise((rs, rj) => {
asc.main(arr, {
stdout: stdout,
stderr: stderr
}, function (err) {
if (err) {
rj(bin2str(stderr.buf))
return
}
rs(stdout.buf)
})
})
}
/**
* 编译合约 ABI
*/
export function compileABI(_str: Binary): ABI[] {
let str = bin2str(_str)
const TYPES = {
u64: 'u64',
i64: 'i64',
f64: 'f64',
bool: 'bool',
string: 'string',
ArrayBuffer: 'bytes',
Address: 'address',
U256: 'u256',
String: 'string',
boolean: 'bool'
}
function getOutputs(str) {
if (str === 'void')
return []
const ret = TYPES[str]
if (!ret)
throw new Error(`invalid type: ${str}`)
return [new TypeDef(ret)]
}
function getInputs(str: string, event?: boolean): TypeDef[] {
const ret = []
for (let p of str.split(',')) {
if (!p)
continue
const lr = p.split(':')
let l = lr[0].trim()
if (event) {
if (!l.startsWith('readonly'))
throw new Error(`event constructor field ${l} should starts with readonly`)
l = l.split(' ')[1]
}
const r = lr[1].trim()
const o = new TypeDef(
TYPES[r],
l
)
if (!o.type)
throw new Error(`invalid type: ${r}`)
ret.push(o)
}
return ret
}
const ret: ABI[] = []
let funRe = /export[\s\n\t]+function[\s\n\t]+([a-zA-Z_][a-zA-Z0-9_]*)[\s\n\t]*\(([a-z\n\s\tA-Z0-9_,:]*)\)[\s\n\t]*:[\s\n\t]*([a-zA-Z_][a-zA-Z0-9_]*)[\s\n\t]*{/g
let eventRe = /@unmanaged[\s\n\t]+class[\s\n\t]+([a-zA-Z_][a-zA-Z0-9]*)[\s\n\t]*\{[\s\n\t]*constructor[\s\n\t]*\(([a-z\n\s\tA-Z0-9_,:]*)\)/g
let contains__idof = false
for (let m of (str.match(funRe) || [])) {
funRe.lastIndex = 0
const r = funRe.exec(m)
if (r[1] === '__idof') {
contains__idof = true
continue
}
ret.push(new ABI(
r[1],
'function',
getInputs(r[2]),
getOutputs(r[3])
))
}
for (let m of (str.match(eventRe) || [])) {
eventRe.lastIndex = 0
const r = eventRe.exec(m)
ret.push(new ABI(
r[1],
'event',
[],
getInputs(r[2], true)
))
}
if (!contains__idof)
throw new Error('any contract must contains an __idof function')
return ret
}
export class TypeDef {
type: string
name?: string
constructor(type: string, name?: string) {
type = type && type.toLocaleLowerCase()
assert(ABI_DATA_TYPE[type] !== undefined, `invalid abi type def name = ${name} type = ${type}`)
this.type = type
this.name = name
}
static from(o): TypeDef {
return new TypeDef(o.type, o.name)
}
}
export class ABI {
name: string
type: ABI_TYPE
inputs: TypeDef[]
outputs: TypeDef[]
constructor(name: string, type: ABI_TYPE, inputs?: TypeDef[], outputs?: TypeDef[]) {
assert(name, 'expect name of abi')
assert(type === 'function' || type === 'event', `invalid abi type ${type}`)
assert(!inputs || Array.isArray(inputs), `invalid inputs ${inputs}`)
assert(!outputs || Array.isArray(outputs), `invalid inputs ${outputs}`)
this.name = name
this.type = type
this.inputs = (inputs || []).map(TypeDef.from)
this.outputs = (outputs || []).map(TypeDef.from)
}
static from(o: any) {
return new ABI(o.name, o.type, o.inputs, o.outputs)
}
// able to return object instead of array
returnsObj(): boolean {
return this.outputs.every(v => v.name) && (
(new Set(this.outputs.map(v => v.name))).size === this.outputs.length
)
}
// able to input object instead of array
inputsObj(): boolean {
return this.inputs.every(v => v.name) && (
(new Set(this.inputs.map(v => v.name))).size === this.inputs.length
)
}
toObj(arr: AbiInput[], input: boolean): Record<string, AbiInput> {
const p = input ? this.inputs : this.outputs
const o = {}
for (let i = 0; i < p.length; i++) {
o[p[i].name] = arr[i]
}
return o
}
toArr(obj: Record<string, AbiInput>, input: boolean): AbiInput[] {
const p = input ? this.inputs : this.outputs
const arr = []
for (let i = 0; i < p.length; i++) {
arr.push(obj[p[i].name])
}
return arr
}
}
export function normalizeParams(params?: AbiInput | AbiInput[] | Record<string, AbiInput>): AbiInput[] | Record<string, AbiInput> {
if (params === null || params === undefined)
return []
if (typeof params === 'bigint' || typeof params === 'string' || typeof params === 'boolean' || typeof params === 'number' || params instanceof ArrayBuffer || params instanceof Uint8Array || params instanceof BN)
return [params]
return params
}
function abiDecode(outputs: TypeDef[], buf?: Uint8Array[]): Readable[] | Record<string, Readable> {
buf = buf || []
const len = buf.length
if (len === 0)
return []
const arr = buf
const returnObject =
outputs.every(v => v.name) && (
(new Set(outputs.map(v => v.name))).size === outputs.length
)
if (arr.length != outputs.length)
throw new Error(`abi decode failed , expect ${outputs.length} returns while ${arr.length} found`)
const ret = returnObject ? {} : []
for (let i = 0; i < arr.length; i++) {
const t = outputs[i].type
const name = outputs[i].name
let val: Readable
switch (t) {
case 'bytes': {
val = bin2hex(arr[i])
break
}
case 'address': {
val = publicKeyHash2Address(arr[i])
break
}
case 'u256':
case 'u64': {
const n = new BN(arr[i])
if (t === 'u64')
assert(n.cmp(MAX_U64) <= 0, `${n.toString(10)} overflows max u64 ${MAX_U64.toString(10)}`)
if (t === 'u256')
assert(n.cmp(MAX_U256) <= 0, `${n.toString(10)} overflows max u256 ${MAX_U256.toString(10)}`)
val = toSafeInt(n)
break
}
case 'i64': {
let n
const padded = padPrefix(arr[i], 0, 8)
const isneg = padded[0] & 0x80
if (!isneg) {
n = new BN(arr[i])
} else {
n = new BN(inverse(padded))
n = n.add(ONE)
n = n.neg()
}
val = toSafeInt(n)
break
}
case 'f64': {
val = bytesToF64(arr[i])
break
}
case 'string': {
val = bin2str(arr[i])
break
}
case 'bool': {
val = arr[i].length > 0
break
}
}
if (returnObject)
ret[name] = val
else
ret[i] = val
}
return ret
}
class MemoryOutputStream implements OutputStream {
buf: Uint8Array
constructor() {
this.buf = new Uint8Array()
}
write(chunk: string | Uint8Array): void {
if (typeof chunk === 'string')
this.buf = concatBytes(this.buf, str2bin(chunk))
else
this.buf = concatBytes(this.buf, chunk)
}
}
export class Contract {
address: string
abi: ABI[]
binary: Uint8Array
constructor(address?: Binary, abi?: ABI[], binary?: ArrayBuffer | Uint8Array) {
if (address)
this.address = bin2hex(normalizeAddress(address))
this.abi = (abi || []).map(ABI.from)
if (binary)
this.binary = hex2bin(binary)
}
abiEncode(name: string, li?: AbiInput | AbiInput[] | Record<string, AbiInput>): [ABI_DATA_TYPE[], Array<string | Uint8Array | BN>, ABI_DATA_TYPE[]] {
const func = this.getABI(name, 'function')
let retType = func.outputs && func.outputs[0] && func.outputs[0].type
const retTypes = retType ? [ABI_DATA_TYPE[retType]] : []
if (typeof li === 'string' || typeof li === 'number' || li instanceof BN || li instanceof ArrayBuffer || li instanceof Uint8Array || typeof li === 'boolean' || typeof li === 'bigint')
return this.abiEncode(name, [li])
if (li === undefined || li === null)
return [[], [], retTypes]
if (Array.isArray(li)) {
const arr = []
const types = []
if (li.length != func.inputs.length)
throw new Error(`abi encode failed for ${func.name}, expect ${func.inputs.length} parameters while ${li.length} found`)
for (let i = 0; i < li.length; i++) {
const t = ABI_DATA_TYPE[func.inputs[i].type]
arr[i] = convert(li[i], t)
types[i] = t
}
return [types, arr, retTypes]
}
const arr: Array<string | Uint8Array | BN> = []
const types: ABI_DATA_TYPE[] = []
for (let i = 0; i < func.inputs.length; i++) {
const input = func.inputs[i]
types[i] = ABI_DATA_TYPE[func.inputs[i].type]
if (!(input.name in li)) {
throw new Error(`key ${input.name} not found in parameters`)
}
arr[i] = convert(li[input.name], ABI_DATA_TYPE[input.type])
}
return [types, arr, retTypes]
}
abiDecode(name: string, buf?: Uint8Array[], type?: ABI_TYPE): Readable | Readable[] | Record<string, Readable> {
type = type || 'function'
buf = buf || []
if (buf.length === 0)
return []
const a = this.getABI(name, type)
const ret = abiDecode(a.outputs, buf)
if (type === 'function')
return ret && ret[0]
return ret
}
/**
* 合约部署的 paylod
*/
abiToBinary(): any[] {
return abiToBinary(this.abi)
}
getABI(name: string, type: ABI_TYPE): ABI {
const funcs = this.abi.filter(x => x.type === type && x.name === name)
assert(funcs.length === 1, `exact exists one and only one abi ${name}, while found ${funcs.length}`)
return funcs[0]
}
}