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。
607 lines (547 loc) • 18.3 kB
text/typescript
import { isZero, MemoryView, VirtualMachine } from './vm'
import { concatBytes, digest, encodeBE, padPrefix } from "./utils"
import { bin2hex, bin2str, hex2bin, str2bin } from './utils'
import BN = require('../bn')
import { MAX_U256, ZERO } from './types'
import { abiToBinary, Contract } from './contract'
import * as rlp from './rlp'
export abstract class AbstractHost {
instance: WebAssembly.Instance = null
view: MemoryView
world: VirtualMachine
utf8Decoder: TextDecoder
utf16Decoder: TextDecoder
nonce: number
deploy: boolean
memory: ArrayBuffer
constructor(world: VirtualMachine) {
this.world = world
this.utf8Decoder = new TextDecoder('utf-8')
this.utf16Decoder = new TextDecoder('utf-16')
}
init(env: { memory: WebAssembly.Memory }): void {
this.view = new MemoryView(env.memory)
}
abstract execute(args: (number | bigint)[]): void | number | bigint
abstract name(): string
}
export class Log extends AbstractHost {
name(): string {
return '_log'
}
execute(args: (number | bigint)[]): void {
console.log(this.view.loadUTF8(args[0], args[1]))
}
}
export class Abort extends AbstractHost {
execute(args: (number | bigint)[]): void {
let msg = isZero(args[0]) ? '' : this.view.loadUTF16(args[0])
let file = isZero(args[1]) ? '' : this.view.loadUTF16(args[1])
throw new Error(`${file} ${msg} error at line ${args[2]} column ${args[3]}`)
}
name(): string {
return 'abort'
}
}
enum UtilType {
CONCAT_BYTES,
DECODE_HEX,
ENCODE_HEX,
BYTES_TO_U64,
U64_TO_BYTES
}
export class Util extends AbstractHost {
execute(args: bigint[]): bigint {
let t = Number(args[0])
let put = !isZero(args[6])
let data: ArrayBuffer = null
let ret = BigInt(0)
switch (t) {
case UtilType.CONCAT_BYTES: {
let a = this.view.loadN(args[1], args[2])
let b = this.view.loadN(args[3], args[4])
data = concatBytes(new Uint8Array(a), new Uint8Array(b)).buffer
ret = BigInt(data.byteLength)
break
}
case UtilType.DECODE_HEX: {
let a = this.view.loadN(args[1], args[2])
let str = bin2str(a)
data = hex2bin(str).buffer
ret = BigInt(data.byteLength)
break
}
case UtilType.ENCODE_HEX: {
let a = this.view.loadN(args[1], args[2])
let str = bin2hex(a)
data = str2bin(str)
ret = BigInt(data.byteLength)
break
}
case UtilType.BYTES_TO_U64: {
let a = new Uint8Array(this.view.loadN(args[1], args[2]))
let b = padPrefix(a, 0, 8)
ret = new DataView(b.buffer).getBigUint64(0, false)
break
}
case UtilType.U64_TO_BYTES: {
data = encodeBE(args[1])
ret = BigInt(data.byteLength)
break
}
}
if (put) {
this.view.put(args[5], data)
}
return ret
}
name(): string {
return '_util'
}
}
enum ContextType {
HEADER_PARENT_HASH,
HEADER_CREATED_AT,
HEADER_HEIGHT,
TX_TYPE,
TX_CREATED_AT,
TX_NONCE,
TX_ORIGIN,
TX_GAS_PRICE,
TX_AMOUNT,
TX_TO,
TX_SIGNATURE,
TX_HASH,
CONTRACT_ADDRESS,
CONTRACT_NONCE,
ACCOUNT_NONCE,
ACCOUNT_BALANCE,
MSG_SENDER,
MSG_AMOUNT,
CONTRACT_CODE,
CONTRACT_ABI
}
export interface CallContext {
type: number
sender: ArrayBuffer
to: ArrayBuffer
amount: BN
nonce: number
origin: ArrayBuffer
txHash: ArrayBuffer
contractAddress: ArrayBuffer
readonly: boolean
}
enum DBType {
SET, GET, REMOVE, HAS, NEXT, CURRENT_KEY, CURRENT_VALUE, HAS_NEXT, RESET
}
enum Algorithm {
KECCAK256
}
export class HashHost extends AbstractHost {
execute(args: bigint[]): bigint {
let bin = this.view.loadN(args[1], args[2])
let t = Number(args[0])
let ret: ArrayBuffer
switch (t) {
case Algorithm.KECCAK256: {
ret = digest(bin).buffer
break
}
default:
throw new Error(`hash host: invalid type ${t}`)
}
if (!isZero(args[4]))
this.view.put(args[3], ret)
return BigInt(ret.byteLength)
}
name(): string {
return '_hash'
}
}
export class EventHost extends AbstractHost {
ctx: CallContext
constructor(world: VirtualMachine, ctx: CallContext) {
super(world)
this.ctx = ctx
}
execute(args: bigint[]): void {
const name = this.view.loadUTF8(args[0], args[1])
let abi = this.world.abiCache.get(bin2hex(this.ctx.contractAddress))
const c = new Contract('', abi)
let fields = <Uint8Array[]>rlp.decode(this.view.loadN(args[2], args[3]))
let o = c.abiDecode(name, fields, 'event')
console.log(`Event emit, name = ${name}`)
console.log(o)
}
name(): string {
return '_event'
}
}
export class DBHost extends AbstractHost {
ctx: CallContext
execute(args: bigint[]): bigint {
let t = Number(args[0])
switch (t) {
case DBType.SET: {
let addr = bin2hex(this.ctx.contractAddress)
let k = this.view.loadN(args[1], args[2])
let val = this.view.loadN(args[3], args[4])
let m = this.world.storage.get(addr) || new Map<string, ArrayBuffer>()
m.set(bin2hex(k), val)
this.world.storage.set(addr, m)
return BigInt(0)
}
case DBType.GET: {
let addr = bin2hex(this.ctx.contractAddress)
let k = bin2hex(this.view.loadN(args[1], args[2]))
let m = this.world.storage.get(addr) || new Map<string, ArrayBuffer>()
if (!m.has(k))
throw new Error(`key ${k} not found in db`)
let val = m.get(k)
if (!isZero(args[4])){
this.view.put(args[3], val)
}
return BigInt(val.byteLength)
}
case DBType.HAS: {
let addr = bin2hex(this.ctx.contractAddress)
let k = bin2hex(this.view.loadN(args[1], args[2]))
let m = this.world.storage.get(addr) || new Map<string, ArrayBuffer>()
return m.has(k) ? BigInt(1) : BigInt(0)
}
case DBType.NEXT:
case DBType.CURRENT_KEY:
case DBType.HAS_NEXT:
case DBType.CURRENT_VALUE:
case DBType.RESET: {
throw new Error('not implemented yet')
}
}
}
name(): string {
return '_db'
}
constructor(world: VirtualMachine, ctx: CallContext) {
super(world)
this.ctx = ctx
}
}
export class ContextHost extends AbstractHost {
ctx: CallContext
constructor(world: VirtualMachine, ctx: CallContext) {
super(world)
this.ctx = ctx
}
execute(args: bigint[]): bigint {
let type = Number(args[0])
let ret = BigInt(0)
let put = !isZero(args[2])
let data: ArrayBuffer = null
let offset = args[1]
switch (type) {
case ContextType.HEADER_PARENT_HASH: {
data = this.world.parentHash
ret = BigInt(data.byteLength)
break
}
case ContextType.HEADER_CREATED_AT: {
put = false
ret = BigInt(this.world.now)
break
}
case ContextType.HEADER_HEIGHT: {
put = false
ret = BigInt(this.world.height)
break
}
case ContextType.TX_TYPE: {
put = false
ret = BigInt(this.ctx.type)
break
}
case ContextType.TX_CREATED_AT: {
put = false
break
}
case ContextType.TX_ORIGIN: {
data = this.ctx.origin
ret = BigInt(data.byteLength)
break
}
case ContextType.TX_GAS_PRICE: {
break
}
case ContextType.TX_AMOUNT: {
data = encodeBE(this.ctx.amount).buffer
ret = BigInt(data.byteLength)
break
}
case ContextType.TX_TO: {
data = this.ctx.to
ret = BigInt(data.byteLength)
break
}
case ContextType.TX_SIGNATURE: {
data = (new Uint8Array(32)).buffer
ret = BigInt(data.byteLength)
break
}
case ContextType.TX_HASH: {
data = this.ctx.txHash
ret = BigInt(data.byteLength)
break
}
case ContextType.CONTRACT_ADDRESS: {
data = this.ctx.contractAddress
ret = BigInt(data.byteLength)
break
}
case ContextType.CONTRACT_NONCE: {
put = false
ret = BigInt(this.world.nonceMap.get(bin2hex(this.ctx.contractAddress)) || 0)
break
}
case ContextType.ACCOUNT_NONCE: {
put = false
let addr = bin2hex(this.view.loadN(args[1], args[2]))
ret = BigInt(this.world.nonceMap.get(bin2hex(addr)) || 0)
break
}
case ContextType.ACCOUNT_BALANCE: {
let addr = bin2hex(this.view.loadN(args[1], args[2]))
let b = this.world.balanceMap.get(bin2hex(addr)) || ZERO
data = encodeBE(b).buffer
offset = args[3]
put = !isZero(args[4])
ret = BigInt(data.byteLength)
break
}
case ContextType.MSG_SENDER: {
data = this.ctx.sender
ret = BigInt(data.byteLength)
break
}
case ContextType.MSG_AMOUNT: {
data = encodeBE(this.ctx.amount).buffer
ret = BigInt(data.byteLength)
break
}
case ContextType.CONTRACT_CODE: {
let addr = bin2hex(this.view.loadN(args[1], args[2]))
let code = this.world.contractCode.get(addr)
data = str2bin(code)
put = !isZero(args[4])
offset = args[3]
break
}
case ContextType.CONTRACT_ABI: {
let abi = this.world.abiCache.get(bin2hex(this.ctx.contractAddress))
data = rlp.encode(abiToBinary(abi))
ret = BigInt(data.byteLength)
put = !isZero(args[4])
offset = args[3]
break
}
}
if (put)
this.view.put(offset, data)
return ret
}
name(): string {
return '_context'
}
}
enum RLPType {
ENCODE_U64,
ENCODE_BYTES,
DECODE_BYTES,
RLP_LIST_SET, // add rlp list to global env
RLP_LIST_CLEAR, // clear rlp list
RLP_LIST_LEN,
RLP_LIST_GET,
RLP_LIST_PUSH,
RLP_LIST_BUILD // build
}
export class RLPHost extends AbstractHost{
list: rlp.RLPList
elements: Uint8Array[]
elementsEncoded: ArrayBuffer
execute(args: bigint[]): bigint {
const t = Number(args[0])
let ret = BigInt(0)
let put = !isZero(args[4])
let data: ArrayBuffer
switch (t){
case RLPType.ENCODE_U64: {
data = rlp.encode(args[1]).buffer
ret = BigInt(data.byteLength)
break
}
case RLPType.ENCODE_BYTES: {
let before = this.view.loadN(args[1], args[2])
data = rlp.encode(before).buffer
ret = BigInt(data.byteLength)
break
}
case RLPType.DECODE_BYTES: {
let encoded = this.view.loadN(args[1], args[2])
let decoded = rlp.decode(encoded)
if(!(decoded instanceof Uint8Array))
throw new Error('rlp decode failed, not a rlp item')
data = (<Uint8Array> decoded).buffer
ret = BigInt(data.byteLength)
break
}
case RLPType.RLP_LIST_SET: {
put = false
this.list = rlp.RLPList.fromEncoded(this.view.loadN(args[1], args[2]))
break
}
case RLPType.RLP_LIST_CLEAR: {
put = false
this.list = null;
break
}
case RLPType.RLP_LIST_LEN: {
put = false
ret = BigInt(this.list.elements.length)
break
}
case RLPType.RLP_LIST_GET: {
data = this.list.raw(Number(args[1]))
ret = BigInt(data.byteLength)
break
}
case RLPType.RLP_LIST_PUSH: {
put = false;
if (!this.elements)
this.elements = []
this.elementsEncoded = null;
let bytes = this.view.loadN(args[1], args[2])
this.elements.push(new Uint8Array(bytes))
break
}
case RLPType.RLP_LIST_BUILD: {
if (!this.elementsEncoded)
this.elementsEncoded = rlp.encodeElements(this.elements).buffer
data = this.elementsEncoded;
ret = BigInt(data.byteLength)
if (!isZero(args[4])) {
this.elementsEncoded = null;
this.elements = null;
}
break
}
default: {
throw new Error(`rlp: unknown type: ${t}`)
}
}
if (put)
this.view.put(args[3], data)
return ret
}
name(): string {
return '_rlp';
}
}
export class Reflect extends AbstractHost{
execute(args: (number | bigint)[]): number | bigint | void {
throw new Error('Method not implemented.')
}
name(): string {
return '_reflect'
}
}
export class Transfer extends AbstractHost{
ctx: CallContext
constructor(world: VirtualMachine, ctx: CallContext) {
super(world)
this.ctx = ctx
}
execute(args: bigint[]): void {
if(!isZero(args[0]))
throw new Error('transfer: unexpected')
let amount = new BN(new Uint8Array(this.view.loadN(args[3], args[4])), 10, 'be')
let to = this.view.loadN(args[1], args[2])
this.world.subBalance(this.ctx.contractAddress, amount)
this.world.addBalance(to, amount)
}
name(): string {
return '_transfer'
}
}
enum Uint256Type {
PARSE,
TOSTRING,
ADD,
SUB,
MUL,
DIV,
MOD
}
function mod(n: BN): BN{
while(n.isNeg())
n = n.add(MAX_U256)
return n.mod(MAX_U256)
}
export class Uint256Host extends AbstractHost{
execute(args: bigint[]): bigint {
const t = Number(args[0])
let data: ArrayBuffer
let put = !isZero(args[6])
let ret = BigInt(0)
let offset = args[5]
switch(t){
case Uint256Type.ADD: {
data = encodeBE(mod(this.getX(args).add(this.getY(args))))
ret = BigInt(data.byteLength)
break
}
case Uint256Type.SUB: {
data = encodeBE(mod(this.getX(args).sub(this.getY(args))))
ret = BigInt(data.byteLength)
break
}
case Uint256Type.MUL: {
data = encodeBE(mod(this.getX(args).mul(this.getY(args))))
ret = BigInt(data.byteLength)
break
}
case Uint256Type.DIV: {
data = encodeBE(mod(this.getX(args).div(this.getY(args))))
ret = BigInt(data.byteLength)
break
}
case Uint256Type.MOD: {
data = encodeBE(mod(this.getX(args).mod(this.getY(args))))
ret = BigInt(data.byteLength)
break
}
case Uint256Type.PARSE: {
let str = this.view.loadUTF8(args[1], args[2])
let radix = Number(args[3])
data = encodeBE(mod(new BN(str, radix)))
ret = BigInt(data.byteLength)
break
}
case Uint256Type.TOSTRING: {
data = str2bin(this.getX(args).toString(Number(args[3]))).buffer
ret = BigInt(data.byteLength)
break
}
}
if(put)
this.view.put(offset, data)
return ret
}
name(): string {
return '_u256'
}
getX(args: bigint[]): BN{
return new BN(new Uint8Array(this.view.loadN(args[1], args[2])), 10, 'be')
}
getY(args: bigint[]): BN{
return new BN(new Uint8Array(this.view.loadN(args[3], args[4])), 10, 'be')
}
}