Skip to content

Commit

Permalink
Merge branch 'master' into 3.0.0-preview2
Browse files Browse the repository at this point in the history
  • Loading branch information
erikzhang authored Apr 18, 2020
2 parents b23c1e9 + 5adfcd5 commit 94d0e50
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 10 deletions.
29 changes: 29 additions & 0 deletions src/RpcClient/Nep5API.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Neo.Cryptography.ECC;
using Neo.Network.P2P.Payloads;
using Neo.Network.RPC.Models;
using Neo.SmartContract;
using Neo.VM;
using Neo.Wallets;
using System;
using System.Linq;
using System.Numerics;
using static Neo.Helper;
Expand Down Expand Up @@ -117,5 +119,32 @@ public Transaction CreateTransferTx(UInt160 scriptHash, KeyPair fromKey, UInt160

return tx;
}

/// <summary>
/// Create NEP5 token transfer transaction from multi-sig account
/// </summary>
/// <param name="scriptHash">contract script hash</param>
/// <param name="m">multi-sig min signature count</param>
/// <param name="pubKeys">multi-sig pubKeys</param>
/// <param name="fromKeys">sign keys</param>
/// <param name="to">to account</param>
/// <param name="amount">transfer amount</param>
/// <returns></returns>
public Transaction CreateTransferTx(UInt160 scriptHash, int m, ECPoint[] pubKeys, KeyPair[] fromKeys, UInt160 to, BigInteger amount)
{
if (m > fromKeys.Length)
throw new ArgumentException($"Need at least {m} KeyPairs for signing!");
var sender = Contract.CreateMultiSigContract(m, pubKeys).ScriptHash;
Cosigner[] cosigners = new[] { new Cosigner { Scopes = WitnessScope.CalledByEntry, Account = sender } };

byte[] script = scriptHash.MakeScript("transfer", sender, to, amount);
Transaction tx = new TransactionManager(rpcClient, sender)
.MakeTransaction(script, null, cosigners)
.AddMultiSig(fromKeys, m, pubKeys)
.Sign()
.Tx;

return tx;
}
}
}
17 changes: 11 additions & 6 deletions src/RpcClient/RpcClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,14 @@ public UInt256 SubmitBlock(byte[] block)
/// Returns the result after calling a smart contract at scripthash with the given operation and parameters.
/// This RPC call does not affect the blockchain in any way.
/// </summary>
public RpcInvokeResult InvokeFunction(string scriptHash, string operation, RpcStack[] stacks)
public RpcInvokeResult InvokeFunction(string scriptHash, string operation, RpcStack[] stacks, params UInt160[] scriptHashesForVerifying)
{
return RpcInvokeResult.FromJson(RpcSend("invokefunction", scriptHash, operation, stacks.Select(p => p.ToJson()).ToArray()));
List<JObject> parameters = new List<JObject> { scriptHash, operation, stacks.Select(p => p.ToJson()).ToArray() };
if (scriptHashesForVerifying.Length > 0)
{
parameters.Add(scriptHashesForVerifying.Select(p => (JObject)p.ToString()).ToArray());
}
return RpcInvokeResult.FromJson(RpcSend("invokefunction", parameters.ToArray()));
}

/// <summary>
Expand All @@ -327,11 +332,11 @@ public RpcInvokeResult InvokeFunction(string scriptHash, string operation, RpcSt
/// </summary>
public RpcInvokeResult InvokeScript(byte[] script, params UInt160[] scriptHashesForVerifying)
{
List<JObject> parameters = new List<JObject>
List<JObject> parameters = new List<JObject> { script.ToHexString() };
if (scriptHashesForVerifying.Length > 0)
{
script.ToHexString()
};
parameters.AddRange(scriptHashesForVerifying.Select(p => (JObject)p.ToString()));
parameters.Add(scriptHashesForVerifying.Select(p => (JObject)p.ToString()).ToArray());
}
return RpcInvokeResult.FromJson(RpcSend("invokescript", parameters.ToArray()));
}

Expand Down
16 changes: 15 additions & 1 deletion src/RpcClient/TransactionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ public TransactionManager MakeTransaction(byte[] script, TransactionAttribute[]
UInt160[] hashes = Tx.GetScriptHashesForVerifying(null);
RpcInvokeResult result = rpcClient.InvokeScript(script, hashes);
Tx.SystemFee = Math.Max(long.Parse(result.GasConsumed) - ApplicationEngine.GasFree, 0);

context = new ContractParametersContext(Tx);
signStore = new List<SignItem>();

Expand Down Expand Up @@ -141,6 +140,21 @@ public TransactionManager AddMultiSig(KeyPair key, int m, params ECPoint[] publi
return this;
}

/// <summary>
/// Add Multi-Signature
/// </summary>
/// <param name="keys">The KeyPairs to sign transction</param>
/// <param name="m">The least count of signatures needed for multiple signature contract</param>
/// <param name="publicKeys">The Public Keys construct the multiple signature contract</param>
public TransactionManager AddMultiSig(KeyPair[] keys, int m, params ECPoint[] publicKeys)
{
foreach (var key in keys)
{
AddMultiSig(key, m, publicKeys);
}
return this;
}

private void AddSignItem(Contract contract, KeyPair key)
{
if (!Tx.GetScriptHashesForVerifying(null).Contains(contract.ScriptHash))
Expand Down
2 changes: 1 addition & 1 deletion src/RpcClient/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public static UInt160 GetScriptHash(string account)
/// <param name="amount">float value</param>
/// <param name="decimals">token decimals</param>
/// <returns></returns>
internal static BigInteger ToBigInteger(this decimal amount, uint decimals)
public static BigInteger ToBigInteger(this decimal amount, uint decimals)
{
BigInteger factor = BigInteger.Pow(10, (int)decimals);
var (numerator, denominator) = Fraction(amount);
Expand Down
20 changes: 19 additions & 1 deletion src/RpcClient/WalletAPI.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Neo.Cryptography.ECC;
using Neo.Ledger;
using Neo.Network.P2P.Payloads;
using Neo.Network.RPC.Models;
Expand Down Expand Up @@ -142,7 +143,7 @@ public Transaction Transfer(string tokenHash, string fromKey, string toAddress,
}

/// <summary>
/// Transfer NEP5 token balance
/// Transfer NEP5 token from single-sig account
/// </summary>
/// <param name="scriptHash">contract script hash</param>
/// <param name="from">from KeyPair</param>
Expand All @@ -156,6 +157,23 @@ public Transaction Transfer(UInt160 scriptHash, KeyPair from, UInt160 to, BigInt
return transaction;
}

/// <summary>
/// Transfer NEP5 token from multi-sig account
/// </summary>
/// <param name="scriptHash">contract script hash</param>
/// <param name="m">multi-sig min signature count</param>
/// <param name="pubKeys">multi-sig pubKeys</param>
/// <param name="keys">sign keys</param>
/// <param name="to">to account</param>
/// <param name="amountInteger">transfer amount</param>
/// <returns></returns>
public Transaction Transfer(UInt160 scriptHash, int m, ECPoint[] pubKeys, KeyPair[] keys, UInt160 to, BigInteger amountInteger)
{
Transaction transaction = nep5API.CreateTransferTx(scriptHash, m, pubKeys, keys, to, amountInteger);
rpcClient.SendRawTransaction(transaction);
return transaction;
}

/// <summary>
/// Wait until the transaction is observable block chain
/// </summary>
Expand Down
3 changes: 2 additions & 1 deletion tests/Neo.Network.RPC.Tests/UT_TransactionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,10 @@ public void TestSign()
byte[] signature = tx.Witnesses[0].InvocationScript.Skip(2).ToArray();

Assert.IsTrue(Crypto.VerifySignature(tx.GetHashData(), signature, keyPair1.PublicKey.EncodePoint(false).Skip(1).ToArray()));
// verify network fee
// verify network fee and system fee
long networkFee = tx.Size * (long)1000 + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null, null);
Assert.AreEqual(networkFee, tx.NetworkFee);
Assert.AreEqual(100, tx.SystemFee);

// duplicate sign should not add new witness
txManager.AddSignature(keyPair1).Sign();
Expand Down
24 changes: 24 additions & 0 deletions tests/Neo.Network.RPC.Tests/UT_WalletAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ public class UT_WalletAPI
string address1;
UInt160 sender;
WalletAPI walletAPI;
UInt160 multiSender;

[TestInitialize]
public void TestSetup()
{
keyPair1 = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"));
sender = Contract.CreateSignatureRedeemScript(keyPair1.PublicKey).ToScriptHash();
multiSender = Contract.CreateMultiSigContract(1, keyPair1.PublicKey).ScriptHash;
address1 = Wallets.Helper.ToAddress(sender);
rpcClientMock = UT_TransactionManager.MockRpcClient(sender, new byte[0]);
walletAPI = new WalletAPI(rpcClientMock.Object);
Expand Down Expand Up @@ -104,6 +106,28 @@ public void TestTransfer()
Assert.AreEqual(testScript.ToHexString(), tranaction.Script.ToHexString());
}

[TestMethod]
public void TestTransferfromMultiSigAccount()
{
byte[] balanceScript = NativeContract.GAS.Hash.MakeScript("balanceOf", multiSender);
var balanceResult = new ContractParameter() { Type = ContractParameterType.Integer, Value = BigInteger.Parse("10000000000000000") };

UT_TransactionManager.MockInvokeScript(rpcClientMock, balanceScript, balanceResult);

byte[] decimalsScript = NativeContract.GAS.Hash.MakeScript("decimals");
UT_TransactionManager.MockInvokeScript(rpcClientMock, decimalsScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(8) });

byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", multiSender, UInt160.Zero, NativeContract.GAS.Factor * 100);
UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) });

var json = new JObject();
json["hash"] = UInt256.Zero.ToString();
rpcClientMock.Setup(p => p.RpcSend("sendrawtransaction", It.IsAny<JObject>())).Returns(json);

var tranaction = walletAPI.Transfer(NativeContract.GAS.Hash, 1, new[] { keyPair1.PublicKey }, new[] { keyPair1 }, UInt160.Zero, NativeContract.GAS.Factor * 100);
Assert.AreEqual(testScript.ToHexString(), tranaction.Script.ToHexString());
}

[TestMethod]
public void TestWaitTransaction()
{
Expand Down

0 comments on commit 94d0e50

Please sign in to comment.