private static byte[] calcDigest(Transaction tx, bool needSigns) { string encoded = ""; var settings = new JsonSerializerSettings() { Formatting = Formatting.None, DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, }; if (tx.TxInputs != null) { for (var i = 0; i < tx.TxInputs.Count; i++) { var input = tx.TxInputs[i]; if (input.RefTxid.Length > 0) { encoded += JsonConvert.SerializeObject(input.RefTxid) + "\n"; } encoded += JsonConvert.SerializeObject(input.RefOffset) + "\n"; if (input.FromAddr.Length > 0) { encoded += JsonConvert.SerializeObject(input.FromAddr) + "\n"; } if (input.Amount.Length > 0) { encoded += JsonConvert.SerializeObject(input.Amount) + "\n"; } encoded += JsonConvert.SerializeObject(input.FrozenHeight) + "\n"; } } encoded += JsonConvert.SerializeObject(tx.TxOutputs, settings) + "\n"; if (tx.Desc != null && tx.Desc.Length > 0) { encoded += JsonConvert.SerializeObject(tx.Desc) + "\n"; } encoded += JsonConvert.SerializeObject(tx.Nonce, settings) + "\n"; encoded += JsonConvert.SerializeObject(tx.Timestamp) + "\n"; encoded += JsonConvert.SerializeObject(tx.Version) + "\n"; if (tx.TxInputsExt != null) { for (var i = 0; i < tx.TxInputsExt.Count; i++) { var input = tx.TxInputsExt[i]; encoded += JsonConvert.SerializeObject(input.Bucket) + "\n"; if (input.Key.Length > 0) { encoded += JsonConvert.SerializeObject(input.Key) + "\n"; } if (input.RefTxid.Length > 0) { encoded += JsonConvert.SerializeObject(input.RefTxid) + "\n"; } encoded += JsonConvert.SerializeObject(input.RefOffset) + "\n"; } } if (tx.TxOutputsExt != null) { foreach (var output in tx.TxOutputsExt) { encoded += (JsonConvert.SerializeObject(output.Bucket) + "\n"); if (output.Key.Length > 0) { encoded += (JsonConvert.SerializeObject(output.Key) + "\n"); } if (output.Value.Length > 0) { encoded += (JsonConvert.SerializeObject(output.Value) + "\n"); } } } encoded += (JsonConvert.SerializeObject(tx.ContractRequests, settings) + "\n"); encoded += (JsonConvert.SerializeObject(tx.Initiator) + "\n"); encoded += (JsonConvert.SerializeObject(tx.AuthRequire) + "\n"); if (needSigns) { encoded += (JsonConvert.SerializeObject(tx.InitiatorSigns) + "\n"); encoded += (JsonConvert.SerializeObject(tx.AuthRequireSigns) + "\n"); } encoded += (JsonConvert.SerializeObject(tx.Coinbase) + "\n"); encoded += (JsonConvert.SerializeObject(tx.Autogen) + "\n"); //Console.WriteLine("Debug: digest=\n" + encoded); var encodedBytes = Encoding.ASCII.GetBytes(encoded); return(XCrypto.DoubleSha256(encodedBytes)); }
private Transaction AssembleTx(Pb.UtxoOutput utxo, XCAccount account, List <string> authRequire, string to, BigInteger amount, Pb.InvokeResponse contractInvoke, string desc) { var tx = new Transaction(); // check param if (amount < 0 || account == null) { return(null); } // if have utxo, assemble utxo input/ouput if (utxo != null) { // assemble TxInputs tx.TxInputs = new List <TxInput>(); var total = BigInteger.Parse(utxo.TotalSelected); for (var i = 0; i < utxo.UtxoList.Count; i++) { var utxoItem = utxo.UtxoList[i]; var input = new TxInput { FromAddr = utxoItem.ToAddr.ToByteArray(), Amount = utxoItem.Amount.ToByteArray(), RefTxid = utxoItem.RefTxid.ToByteArray(), RefOffset = utxoItem.RefOffset, }; tx.TxInputs.Add(input); } tx.TxOutputs = new List <TxOutput>(); // Assemble utxo Output for transferring to if (amount > 0 && to != "") { // utxo check if (amount > total) { Console.WriteLine("Utxo use greater than utxo selected" + ", selected=" + total + ", use=", amount); return(null); } var output = new TxOutput() { ToAddr = Encoding.ASCII.GetBytes(to), Amount = amount.ToByteArray(), }; Array.Reverse(output.Amount, 0, output.Amount.Length); tx.TxOutputs.Add(output); total -= amount; } // Assemble contract fee if (contractInvoke != null && contractInvoke.GasUsed > 0) { var gasUsed = new BigInteger(contractInvoke.GasUsed); if (gasUsed > total) { Console.WriteLine("Utxo use greater than utxo selected" + ", selected=" + total + ", use=", gasUsed); return(null); } var output = new TxOutput() { ToAddr = Encoding.ASCII.GetBytes("$"), Amount = gasUsed.ToByteArray(), }; Array.Reverse(output.Amount, 0, output.Amount.Length); tx.TxOutputs.Add(output); total -= gasUsed; } // charge utxo to user if (total > 0) { var chargeOutput = new TxOutput() { ToAddr = Encoding.ASCII.GetBytes(account.Address), Amount = total.ToByteArray(), }; Array.Reverse(chargeOutput.Amount, 0, chargeOutput.Amount.Length); tx.TxOutputs.Add(chargeOutput); } } // Assemble contracts if (contractInvoke != null) { if (contractInvoke.Inputs.Count > 0) { tx.TxInputsExt = new List <TxInputExt>(); } if (contractInvoke.Outputs.Count > 0) { tx.TxOutputsExt = new List <TxOutputExt>(); } if (contractInvoke.Requests.Count > 0) { tx.ContractRequests = new List <InvokeRequest>(); } // TODO: transfer within contract is not supported foreach (var input in contractInvoke.Inputs) { var inputExt = new TxInputExt { Bucket = input.Bucket, Key = input.Key.ToByteArray(), RefTxid = input.RefTxid.ToByteArray(), RefOffset = input.RefOffset, }; tx.TxInputsExt.Add(inputExt); } foreach (var output in contractInvoke.Outputs) { var outputExt = new TxOutputExt { Bucket = output.Bucket, Key = output.Key.ToByteArray(), Value = output.Value.ToByteArray(), }; tx.TxOutputsExt.Add(outputExt); } foreach (var request in contractInvoke.Requests) { var invoke = new InvokeRequest { ModuleName = request.ModuleName, ContractName = request.ContractName, MethodName = request.MethodName, }; foreach (var arg in request.Args) { invoke.Args.Add(arg.Key, arg.Value.ToByteArray()); } foreach (var limit in request.ResourceLimits) { invoke.ResourceLimits.Add(new ResourceLimit { Type = (ResourceType)limit.Type, Limit = limit.Limit, }); } tx.ContractRequests.Add(invoke); } } // Assemble other data tx.Desc = Encoding.ASCII.GetBytes(desc); tx.Version = 1; tx.Coinbase = false; tx.Autogen = false; tx.Initiator = account.Address; if (authRequire != null && authRequire.Count > 0) { tx.AuthRequire = authRequire; } var digestHash = XDigest.MakeDigestHash(tx); var sign = XCrypto.SignHash(account.PrivateKey, digestHash); var signInfo = new SignatureInfo() { PublicKey = account.PublicKey.RawKey, Sign = sign, }; tx.InitiatorSigns = new List <SignatureInfo>(); tx.InitiatorSigns.Add(signInfo); if (authRequire != null && authRequire.Count > 0) { tx.AuthRequireSigns = new List <SignatureInfo>(); tx.AuthRequireSigns.Add(signInfo); } var txid = XDigest.MakeTransactionID(tx); tx.Txid = txid; return(tx); }