UNPKG

nel-neo-thinsdk

Version:
546 lines (499 loc) 20.5 kB
///<reference path="helper.ts"/> namespace ThinNeo { export enum TransactionType { /// <summary> /// 用于分配字节费的特殊交易 /// </summary> MinerTransaction = 0x00, /// <summary> /// 用于分发资产的特殊交易 /// </summary> IssueTransaction = 0x01, /// <summary> /// 用于分配小蚁币的特殊交易 /// </summary> ClaimTransaction = 0x02, /// <summary> /// 用于报名成为记账候选人的特殊交易 /// </summary> EnrollmentTransaction = 0x20, /// <summary> /// 用于资产登记的特殊交易 /// </summary> RegisterTransaction = 0x40, /// <summary> /// 合约交易,这是最常用的一种交易 /// </summary> ContractTransaction = 0x80, /// <summary> /// Publish scripts to the blockchain for being invoked later. /// </summary> PublishTransaction = 0xd0, InvocationTransaction = 0xd1 } export enum TransactionAttributeUsage { /// <summary> /// 外部合同的散列值 /// </summary> ContractHash = 0x00, /// <summary> /// 用于ECDH密钥交换的公钥,该公钥的第一个字节为0x02 /// </summary> ECDH02 = 0x02, /// <summary> /// 用于ECDH密钥交换的公钥,该公钥的第一个字节为0x03 /// </summary> ECDH03 = 0x03, /// <summary> /// 用于对交易进行额外的验证 /// </summary> Script = 0x20, Vote = 0x30, DescriptionUrl = 0x81, Description = 0x90, Hash1 = 0xa1, Hash2 = 0xa2, Hash3 = 0xa3, Hash4 = 0xa4, Hash5 = 0xa5, Hash6 = 0xa6, Hash7 = 0xa7, Hash8 = 0xa8, Hash9 = 0xa9, Hash10 = 0xaa, Hash11 = 0xab, Hash12 = 0xac, Hash13 = 0xad, Hash14 = 0xae, Hash15 = 0xaf, /// <summary> /// 备注 /// </summary> Remark = 0xf0, Remark1 = 0xf1, Remark2 = 0xf2, Remark3 = 0xf3, Remark4 = 0xf4, Remark5 = 0xf5, Remark6 = 0xf6, Remark7 = 0xf7, Remark8 = 0xf8, Remark9 = 0xf9, Remark10 = 0xfa, Remark11 = 0xfb, Remark12 = 0xfc, Remark13 = 0xfd, Remark14 = 0xfe, Remark15 = 0xff } export class Attribute { public usage: TransactionAttributeUsage; public data: Uint8Array; } export class TransactionOutput { public assetId: Uint8Array; public value: Neo.Fixed8; public toAddress: Uint8Array; } export class TransactionInput { public hash: Uint8Array; public index: number; } export class Witness { public InvocationScript: Uint8Array;//设置参数脚本,通常是吧signdata push进去 public VerificationScript: Uint8Array;//校验脚本,通常是 push 公钥, CheckSig 两条指令 验证的东西就是未签名的交易 //这个就是地址的脚本 public get Address(): string { var hash = ThinNeo.Helper.GetScriptHashFromScript(this.VerificationScript); return ThinNeo.Helper.GetAddressFromScriptHash(hash); } } export interface IExtData { Serialize(trans: Transaction, writer: Neo.IO.BinaryWriter): void; Deserialize(trans: Transaction, reader: Neo.IO.BinaryReader): void; } export class InvokeTransData implements IExtData { public script: Uint8Array; public gas: Neo.Fixed8; public Serialize(trans: Transaction, writer: Neo.IO.BinaryWriter): void { writer.writeVarBytes(this.script.buffer); if (trans.version >= 1) { writer.writeUint64(this.gas.getData()); } } public Deserialize(trans: Transaction, reader: Neo.IO.BinaryReader): void { var buf = reader.readVarBytes(10000000); this.script = new Uint8Array(buf, 0, buf.byteLength); if (trans.version >= 1) { this.gas = new Neo.Fixed8(reader.readUint64()); } } } export class ClaimTransData implements IExtData { public claims: TransactionInput[]; public Serialize(trans: Transaction, writer: Neo.IO.BinaryWriter): void { writer.writeVarInt(this.claims.length); for (var i = 0; i < this.claims.length; i++) { writer.write(this.claims[i].hash, 0, 32); writer.writeUint16(this.claims[i].index); } } public Deserialize(trans: Transaction, reader: Neo.IO.BinaryReader): void { var countClaims = reader.readVarInt(); this.claims = [];//new TransactionInput[countInputs]; for (var i = 0; i < countClaims; i++) { this.claims.push(new TransactionInput()); //this.inputs[i] = new TransactionInput(); var arr = reader.readBytes(32); this.claims[i].hash = new Uint8Array(arr, 0, arr.byteLength); this.claims[i].index = reader.readUint16(); } } } export class MinerTransData implements IExtData { public nonce: number; public Serialize(trans: Transaction, writer: Neo.IO.BinaryWriter): void { writer.writeUint32(this.nonce); } public Deserialize(trans: Transaction, reader: Neo.IO.BinaryReader): void { this.nonce = reader.readUint32(); } } export class Transaction { public type: TransactionType; public version: number; public attributes: Attribute[]; public inputs: TransactionInput[]; public outputs: TransactionOutput[]; public witnesses: Witness[];//见证人 public SerializeUnsigned(writer: Neo.IO.BinaryWriter): void { //write type writer.writeByte(this.type as number); //write version writer.writeByte(this.version); //SerializeExclusiveData(writer); if (this.type == TransactionType.ContractTransaction || this.type == TransactionType.IssueTransaction)//每个交易类型有一些自己独特的处理 { //ContractTransaction 就是最常见的转账交易 //他没有自己的独特处理 } else if (this.type == TransactionType.InvocationTransaction) { this.extdata.Serialize(this, writer); } else if (this.type == TransactionType.ClaimTransaction) { this.extdata.Serialize(this, writer); } else if (this.type == TransactionType.MinerTransaction) { this.extdata.Serialize(this, writer); } else { throw new Error("未编写针对这个交易类型的代码"); } //#region write attribute var countAttributes = this.attributes.length; writer.writeVarInt(countAttributes); for (var i = 0; i < countAttributes; i++) { var attributeData = this.attributes[i].data; var Usage = this.attributes[i].usage; writer.writeByte(Usage as number); if (Usage == TransactionAttributeUsage.ContractHash || Usage == TransactionAttributeUsage.Vote || (Usage >= TransactionAttributeUsage.Hash1 && Usage <= TransactionAttributeUsage.Hash15)) { //attributeData =new byte[32]; writer.write(attributeData.buffer, 0, 32); } else if (Usage == TransactionAttributeUsage.ECDH02 || Usage == TransactionAttributeUsage.ECDH03) { //attributeData = new byte[33]; //attributeData[0] = (byte)Usage; writer.write(attributeData.buffer, 1, 32); } else if (Usage == TransactionAttributeUsage.Script) { //attributeData = new byte[20]; writer.write(attributeData.buffer, 0, 20); } else if (Usage == TransactionAttributeUsage.DescriptionUrl) { //var len = (byte)ms.ReadByte(); //attributeData = new byte[len]; var len = attributeData.length; writer.writeByte(len); writer.write(attributeData.buffer, 0, len); } else if (Usage == TransactionAttributeUsage.Description || Usage >= TransactionAttributeUsage.Remark) { //var len = (int)readVarInt(ms, 65535); //attributeData = new byte[len]; var len = attributeData.length; writer.writeVarInt(len); writer.write(attributeData.buffer, 0, len); } else throw new Error(); } //#endregion //#region write Input var countInputs = this.inputs.length; writer.writeVarInt(countInputs); for (var i = 0; i < countInputs; i++) { writer.write(this.inputs[i].hash, 0, 32); writer.writeUint16(this.inputs[i].index); } //#endregion //#region write Outputs var countOutputs = this.outputs.length; writer.writeVarInt(countOutputs); for (var i = 0; i < countOutputs; i++) { var item = this.outputs[i]; //资产种类 writer.write(item.assetId.buffer, 0, 32); writer.writeUint64(item.value.getData()); writer.write(item.toAddress.buffer, 0, 20); } //#endregion } public Serialize(writer: Neo.IO.BinaryWriter): void { this.SerializeUnsigned(writer); var witnesscount = this.witnesses.length; writer.writeVarInt(witnesscount); for (var i = 0; i < witnesscount; i++) { var _witness = this.witnesses[i]; writer.writeVarBytes(_witness.InvocationScript.buffer); writer.writeVarBytes(_witness.VerificationScript.buffer); } } public extdata: IExtData; public DeserializeUnsigned(ms: Neo.IO.BinaryReader): void { //参考源码来自 // https://github.com/neo-project/neo // Transaction.cs // 源码采用c#序列化技术 //参考源码2 // https://github.com/AntSharesSDK/antshares-ts // Transaction.ts // 采用typescript开发 this.type = ms.readByte() as TransactionType;//读一个字节,交易类型 this.version = ms.readByte(); if (this.type == TransactionType.ContractTransaction || this.type == TransactionType.IssueTransaction)//每个交易类型有一些自己独特的处理 { //ContractTransaction 就是最常见的合约交易, //他没有自己的独特处理 this.extdata = null; } else if (this.type == TransactionType.InvocationTransaction) { this.extdata = new InvokeTransData(); } else if (this.type == TransactionType.ClaimTransaction) { this.extdata = new ClaimTransData(); } else if (this.type == TransactionType.MinerTransaction) { this.extdata = new MinerTransData(); } else { throw new Error("未编写针对这个交易类型的代码"); } if (this.extdata != null) { this.extdata.Deserialize(this, ms); } //attributes var countAttributes = ms.readVarInt(); this.attributes = [];//new Attribute[countAttributes]; // Console.WriteLine("countAttributes:" + countAttributes); for (var i = 0; i < countAttributes; i++) { //读取attributes var attributeData: Uint8Array = null; var Usage = ms.readByte() as TransactionAttributeUsage; if (Usage == TransactionAttributeUsage.ContractHash || Usage == TransactionAttributeUsage.Vote || (Usage >= TransactionAttributeUsage.Hash1 && Usage <= TransactionAttributeUsage.Hash15)) { var arr = ms.readBytes(32); attributeData = new Uint8Array(arr, 0, arr.byteLength); } else if (Usage == TransactionAttributeUsage.ECDH02 || Usage == TransactionAttributeUsage.ECDH03) { var arr = ms.readBytes(32); var data = new Uint8Array(arr, 0, arr.byteLength); attributeData = new Uint8Array(33); attributeData[0] = Usage as number; for (var i = 0; i < 32; i++) { attributeData[i + 1] = data[i]; } } else if (Usage == TransactionAttributeUsage.Script) { var arr = ms.readBytes(20); attributeData = new Uint8Array(arr, 0, arr.byteLength); } else if (Usage == TransactionAttributeUsage.DescriptionUrl) { var len = ms.readByte(); var arr = ms.readBytes(len); attributeData = new Uint8Array(arr, 0, arr.byteLength); } else if (Usage == TransactionAttributeUsage.Description || Usage >= TransactionAttributeUsage.Remark) { var len = ms.readVarInt(65535); var arr = ms.readBytes(len); attributeData = new Uint8Array(arr, 0, arr.byteLength); } else throw new Error(); var attr = new Attribute(); attr.usage = Usage; attr.data = attributeData; this.attributes.push(attr); } //inputs 输入表示基于哪些交易 var countInputs = ms.readVarInt(); //Console.WriteLine("countInputs:" + countInputs); this.inputs = [];//new TransactionInput[countInputs]; for (var i = 0; i < countInputs; i++) { this.inputs.push(new TransactionInput()); //this.inputs[i] = new TransactionInput(); var arr = ms.readBytes(32); this.inputs[i].hash = new Uint8Array(arr, 0, arr.byteLength); this.inputs[i].index = ms.readUint16(); } //outputes 输出表示最后有哪几个地址得到多少钱,肯定有一个是自己的地址,因为每笔交易都会把之前交易的余额清空,刨除自己,就是要转给谁多少钱 //这个机制叫做UTXO var countOutputs = ms.readVarInt(); //Console.WriteLine("countOutputs:" + countOutputs); this.outputs = [];//new TransactionOutput[countOutputs]; for (var i = 0; i < countOutputs; i++) { this.outputs.push(new TransactionOutput()); var outp = this.outputs[i]; //资产种类 var arr = ms.readBytes(32); var assetid = new Uint8Array(arr, 0, arr.byteLength); var value = new Neo.Fixed8(ms.readUint64()); //资产数量 var arr = ms.readBytes(20); var scripthash = new Uint8Array(arr, 0, arr.byteLength); outp.assetId = assetid; outp.value = value; outp.toAddress = scripthash; this.outputs[i] = outp; } } public Deserialize(ms: Neo.IO.BinaryReader): void { this.DeserializeUnsigned(ms); if (ms.canRead()>0) { var witnesscount = ms.readVarInt(); this.witnesses = []; for (var i = 0; i < witnesscount; i++) { this.witnesses.push(new Witness()); this.witnesses[i].InvocationScript = new Uint8Array(ms.readVarBytes()).clone(); this.witnesses[i].VerificationScript = new Uint8Array(ms.readVarBytes()).clone(); } } } public GetMessage(): Uint8Array { var ms = new Neo.IO.MemoryStream(); var writer = new Neo.IO.BinaryWriter(ms); this.SerializeUnsigned(writer); var arr = ms.toArray(); var msg = new Uint8Array(arr, 0, arr.byteLength); return msg; } public GetRawData(): Uint8Array { var ms = new Neo.IO.MemoryStream(); var writer = new Neo.IO.BinaryWriter(ms); this.Serialize(writer); var arr = ms.toArray(); var msg = new Uint8Array(arr, 0, arr.byteLength); return msg; } //增加个人账户见证人(就是用这个人的私钥对交易签个名,signdata传进来) public AddWitness(signdata: Uint8Array, pubkey: Uint8Array, addrs: string): void { {//额外的验证 var msg = this.GetMessage(); var bsign = ThinNeo.Helper.VerifySignature(msg, signdata, pubkey); if (bsign == false) throw new Error("wrong sign"); var addr = ThinNeo.Helper.GetAddressFromPublicKey(pubkey); if (addr != addrs) throw new Error("wrong script"); } var vscript = ThinNeo.Helper.GetAddressCheckScriptFromPublicKey(pubkey); //iscript 对个人账户见证人他是一条pushbytes 指令 var sb = new ThinNeo.ScriptBuilder(); sb.EmitPushBytes(signdata); var iscript = sb.ToArray(); this.AddWitnessScript(vscript, iscript); } //增加智能合约见证人 public AddWitnessScript(vscript: Uint8Array, iscript: Uint8Array): void { var scripthash = ThinNeo.Helper.GetScriptHashFromScript(vscript); if (this.witnesses == null) this.witnesses = []; var newwit = new Witness(); newwit.VerificationScript = vscript; newwit.InvocationScript = iscript; for (var i = 0; i < this.witnesses.length; i++) { if (this.witnesses[i].Address == newwit.Address) throw new Error("alread have this witness"); } this.witnesses.push(newwit); } //TXID public GetHash(): Uint8Array { var msg = this.GetMessage(); var data = Neo.Cryptography.Sha256.computeHash(msg); data = Neo.Cryptography.Sha256.computeHash(data); return new Uint8Array(data, 0, data.byteLength); } } }