UNPKG

nel-neo-thinsdk

Version:
233 lines (216 loc) 10.1 kB
namespace ThinNeo { export class ScriptBuilder { writer: number[]; Offset: number = 0; public constructor() { this.writer = []; } _WriteUint8(num: number): void { this.writer.push(num); this.Offset++; } _WriteUint16(num: number): void { var buf = new Uint8Array(2); var d = new DataView(buf.buffer, 0, 2); d.setUint16(0, num, true); this.writer.push(buf[0]); this.writer.push(buf[1]); this.Offset += 2; } _WriteUint32(num: number): void { var buf = new Uint8Array(4); var d = new DataView(buf.buffer, 0, 4); d.setUint32(0, num, true); this.writer.push(buf[0]); this.writer.push(buf[1]); this.writer.push(buf[2]); this.writer.push(buf[3]); this.Offset += 4; } _WriteUint8Array(nums: Uint8Array): void { for (var i = 0; i < nums.length; i++) this.writer.push(nums[i]); this.Offset += nums.length; } _ConvertInt16ToBytes(num: number): Uint8Array { var buf = new Uint8Array(2); var d = new DataView(buf.buffer, 0, 2); d.setInt16(0, num, true); return buf; } public Emit(op: OpCode, arg: Uint8Array = null): ScriptBuilder { this._WriteUint8(op); if (arg != null) this._WriteUint8Array(arg); return this; } public EmitAppCall(scriptHash: Uint8Array, useTailCall: boolean = false): ScriptBuilder { if (scriptHash.length != 20) throw new Error("error scriptHash length"); return this.Emit(useTailCall ? OpCode.TAILCALL : OpCode.APPCALL, scriptHash); } public EmitJump(op: OpCode, offset: number): ScriptBuilder { if (op != OpCode.JMP && op != OpCode.JMPIF && op != OpCode.JMPIFNOT && op != OpCode.CALL) throw new Error("ArgumentException"); return this.Emit(op, this._ConvertInt16ToBytes(offset)); } public EmitPushNumber(number: Neo.BigInteger): ScriptBuilder { var i32 = number.toInt32(); if (i32 == -1) return this.Emit(OpCode.PUSHM1); if (i32 == 0) return this.Emit(OpCode.PUSH0); if (i32 > 0 && i32 <= 16) return this.Emit(OpCode.PUSH1 - 1 + i32); return this.EmitPushBytes(number.toUint8ArrayWithSign(true)); } public EmitPushBool(data: boolean): ScriptBuilder { return this.Emit(data ? OpCode.PUSHT : OpCode.PUSHF); } public EmitPushBytes(data: Uint8Array): ScriptBuilder { if (data == null) throw new Error("ArgumentNullException"); if (data.length <= OpCode.PUSHBYTES75) { this._WriteUint8(data.length); this._WriteUint8Array(data); } else if (data.length < 0x100) { this.Emit(OpCode.PUSHDATA1); this._WriteUint8(data.length); this._WriteUint8Array(data); } else if (data.length < 0x10000) { this.Emit(OpCode.PUSHDATA2); this._WriteUint16(data.length); this._WriteUint8Array(data); } else// if (data.Length < 0x100000000L) { this.Emit(OpCode.PUSHDATA4); this._WriteUint32(data.length); this._WriteUint8Array(data); } return this; } public EmitPushString(data: string): ScriptBuilder { return this.EmitPushBytes(ThinNeo.Helper.String2Bytes(data)); } public EmitSysCall(api: string): ScriptBuilder { if (api == null) throw new Error("ArgumentNullException"); var api_bytes = ThinNeo.Helper.String2Bytes(api); if (api_bytes.length == 0 || api_bytes.length > 252) throw new Error("ArgumentException"); var arg: Uint8Array = new Uint8Array(api_bytes.length + 1); arg[0] = api_bytes.length; for (var i = 0; i < api_bytes.length; i++) { arg[i + 1] = api_bytes[i]; } return this.Emit(OpCode.SYSCALL, arg); } public ToArray(): Uint8Array { var array = new Uint8Array(this.writer.length); for (var i = 0; i < this.writer.length; i++) { array[i] = this.writer[i]; } return array; } //如果参数为string,其实是特殊值 //(string) or(str) 开头,表示是个字符串,utf8编码为bytes //(bytes) or([])开头,表示就是一个bytearray //(address) or(addr)开头,表示是一个地址,转换为脚本hash //(integer) or(int) 开头,表示是一个大整数 //(hexinteger) or (hexint) or (hex) 开头,表示是一个16进制表示的大整数,转换为bytes就是反序 //(int256) or (hex256) 开头,表示是一个定长的256位 16进制大整数 //(int160) or (hex160) 开头,表示是一个定长的160位 16进制大整数 public EmitParamJson(param: any): ScriptBuilder { if (typeof param === "number")//bool 或小整数 { this.EmitPushNumber(new Neo.BigInteger(param as number)); } else if (typeof param === "boolean") { this.EmitPushBool(param as boolean); } else if (typeof param === "object") { var list = param as any[]; for (var i = list.length - 1; i >= 0; i--) { this.EmitParamJson(list[i]); } this.EmitPushNumber(new Neo.BigInteger(list.length)); this.Emit(ThinNeo.OpCode.PACK); } else if (typeof param === "string")//复杂格式 { var str = param as string; if (str[0] != '(') throw new Error("must start with:(str) or (hex) or (hexrev) or (addr)or(int)"); //(string) or(str) 开头,表示是个字符串,utf8编码为bytes if (str.indexOf("(string)") == 0) { this.EmitPushString(str.substr(8)); } if (str.indexOf("(str)") == 0) { this.EmitPushString(str.substr(5)); } //(bytes) or([])开头,表示就是一个bytearray else if (str.indexOf("(bytes)") == 0) { var hex = str.substr(7).hexToBytes(); this.EmitPushBytes(hex); } else if (str.indexOf("([])") == 0) { var hex = str.substr(4).hexToBytes(); this.EmitPushBytes(hex); } //(address) or(addr)开头,表示是一个地址,转换为脚本hash else if (str.indexOf("(address)") == 0) { var addr = (str.substr(9)); var hex = ThinNeo.Helper.GetPublicKeyScriptHash_FromAddress(addr); this.EmitPushBytes(hex); } else if (str.indexOf("(addr)") == 0) { var addr = (str.substr(6)); var hex = ThinNeo.Helper.GetPublicKeyScriptHash_FromAddress(addr); this.EmitPushBytes(hex); } //(integer) or(int) 开头,表示是一个大整数 else if (str.indexOf("(integer)") == 0) { var num = new Neo.BigInteger(str.substr(9)); this.EmitPushNumber(num); } else if (str.indexOf("(int)") == 0) { var num = new Neo.BigInteger(str.substr(5)); this.EmitPushNumber(num); } //(hexinteger) or (hexint) or (hex) 开头,表示是一个16进制表示的大整数,转换为bytes就是反序 else if (str.indexOf("(hexinteger)") == 0) { var hex = str.substr(12).hexToBytes(); this.EmitPushBytes(hex.reverse()); } else if (str.indexOf("(hexint)") == 0) { var hex = str.substr(8).hexToBytes(); this.EmitPushBytes(hex.reverse()); } else if (str.indexOf("(hex)") == 0) { var hex = str.substr(5).hexToBytes(); this.EmitPushBytes(hex.reverse()); } //(int256) or (hex256) 开头,表示是一个定长的256位 16进制大整数 else if (str.indexOf("(int256)") == 0 || str.indexOf("(hex256)") == 0) { var hex = str.substr(8).hexToBytes(); if (hex.length != 32) throw new Error("not a int256"); this.EmitPushBytes(hex.reverse()); } //(int160) or (hex160) 开头,表示是一个定长的160位 16进制大整数 else if (str.indexOf("(int160)") == 0 || str.indexOf("(hex160)") == 0) { var hex = str.substr(8).hexToBytes(); if (hex.length != 20) throw new Error("not a int160"); this.EmitPushBytes(hex.reverse()); } else throw new Error("must start with:(str) or (hex) or (hexbig) or (addr) or(int)"); } else { throw new Error("error type:" + typeof param); } return this; } } }