/// <summary> /// Combine creates a network-specific transaction from an unsigned transaction and an array of provided signatures. /// The signed transaction returned from this method will be sent to the `/construction/submit` endpoint by the caller. /// </summary> /// <param name="request"></param> /// <returns></returns> public JObject ConstructionCombine(ConstructionCombineRequest request) { NeoTransaction neoTx; try { byte[] unsigned = request.UnsignedTransaction.HexToBytes(); TransactionType type = (TransactionType)unsigned[0]; switch (type) { case TransactionType.ClaimTransaction: neoTx = new ClaimTransaction(); break; case TransactionType.ContractTransaction: neoTx = new ContractTransaction(); break; case TransactionType.StateTransaction: neoTx = new StateTransaction(); break; case TransactionType.InvocationTransaction: neoTx = new InvocationTransaction(); break; default: throw new ArgumentException(); } using (MemoryStream ms = new MemoryStream(unsigned, false)) using (BinaryReader br = new BinaryReader(ms, Encoding.UTF8)) { (neoTx as IVerifiable).DeserializeUnsigned(br); } } catch (Exception) { return(Error.TX_DESERIALIZE_ERROR.ToJson()); } if (neoTx.Witnesses != null && neoTx.Witnesses.Length > 0) { return(Error.TX_ALREADY_SIGNED.ToJson()); } if (request.Signatures.Length == 0) { return(Error.NO_SIGNATURE.ToJson()); } Witness witness; if (request.Signatures.Length == 1) // single signed { witness = CreateSignatureWitness(request.Signatures[0]); } else { witness = CreateMultiSignatureWitness(request.Signatures); } if (witness is null) { return(Error.INVALID_SIGNATURES.ToJson()); } neoTx.Witnesses = new Witness[] { witness }; byte[] signed = neoTx.ToArray(); ConstructionCombineResponse response = new ConstructionCombineResponse(signed.ToHexString()); return(response.ToJson()); }
/// <summary> /// Combine creates a network-specific transaction from an unsigned transaction and an array of provided signatures. /// The signed transaction returned from this method will be sent to the `/construction/submit` endpoint by the caller. /// </summary> /// <param name="request"></param> /// <returns></returns> public JObject ConstructionCombine(ConstructionCombineRequest request) { if (request.NetworkIdentifier?.Blockchain?.ToLower() != "neo n3") { return(Error.NETWORK_IDENTIFIER_INVALID.ToJson()); } if (request.NetworkIdentifier?.Network?.ToLower() != network) { return(Error.NETWORK_IDENTIFIER_INVALID.ToJson()); } NeoTransaction neoTx; try { byte[] txRaw = Convert.FromBase64String(request.UnsignedTransaction); // use base64 string in N3 neoTx = txRaw.AsSerializable <NeoTransaction>(); } catch (Exception) { return(Error.TX_DESERIALIZE_ERROR.ToJson()); } if (neoTx.Witnesses == null || neoTx.Witnesses.Length != neoTx.Signers.Length) { return(Error.TX_WITNESSES_INVALID.ToJson()); } if (request.Signatures.Length == 0) { return(Error.NO_SIGNATURE.ToJson()); } var signData = neoTx.GetSignData(system.Settings.Network); for (int i = 0; i < neoTx.Signers.Length; i++) { var witness = neoTx.Witnesses[i]; if (witness.VerificationScript.IsSignatureContract()) { var sign = request.Signatures.First(s => s.PublicKey.AddressHash == neoTx.Signers[i].Account); CreateSignatureWitness(witness, sign, signData); } else if (witness.VerificationScript.IsMultiSigContract(out var m, out ECPoint[] points)) { var pubKeys = points.OrderBy(p => p).Select(p => p.ToUInt160FromPublicKey()).ToList(); var signs = new List <byte[]>(); var count = 0; foreach (var pubkeyhash in pubKeys) { if (count == m) { break; } var sign = request.Signatures.First(s => s.PublicKey.AddressHash == pubkeyhash); signs.Add(sign.HexBytes.HexToBytes()); count++; } var mulWitness = new Witness(); using (ScriptBuilder sb = new ScriptBuilder()) { signs.ForEach(p => sb.EmitPush(p)); witness.InvocationScript = sb.ToArray(); } } } byte[] signed = neoTx.ToArray(); ConstructionCombineResponse response = new ConstructionCombineResponse(Convert.ToBase64String(signed)); return(response.ToJson()); }