Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using CSharpFunctionalExtensions;
using Microsoft.Extensions.Logging;
using Moq;
using NBitcoin;
using NBitcoin.DataEncoders;
using Stratis.Bitcoin.Configuration.Logging;
using Stratis.Bitcoin.Features.SmartContracts.Models;
using Stratis.Bitcoin.Features.SmartContracts.Wallet;
Expand Down Expand Up @@ -974,5 +976,107 @@ public void BuildFeeEstimationContext_Recipient_Is_Not_P2PKH()
)));
}
*/

[Fact]
public void CanPassSignatures()
{
const int utxoIndex = 0;
uint256 utxoId = uint256.Zero;
uint256 utxoIdUnused = uint256.One;
string senderAddress = uint160.Zero.ToBase58Address(this.network);
string contractAddress = uint160.One.ToBase58Address(this.network);

var key1 = new Key();
var key2 = new Key();
var msg = "This is a test challenge";
var sig1 = key1.SignMessage(msg);
var sig2 = key2.SignMessage(msg);

var request = new BuildCallContractTransactionRequest
{
Amount = "0",
AccountName = "account 0",
ContractAddress = contractAddress,
FeeAmount = "0.01",
GasLimit = 100_000,
GasPrice = 100,
MethodName = "TestMethod",
WalletName = "wallet",
Password = "password",
Sender = senderAddress,
Outpoints = new List<OutpointRequest>
{
new OutpointRequest
{
Index = utxoIndex,
TransactionId = utxoId.ToString()
},
},
Parameters = new[] { "SIG#" },
Signatures = new[] { sig1, sig2 }
};

this.walletManager.Setup(x => x.GetAddressBalance(request.Sender))
.Returns(new AddressBalance
{
Address = senderAddress,
AmountConfirmed = new Money(100, MoneyUnit.BTC)
});

this.walletManager.Setup(x => x.GetSpendableTransactionsInWallet(It.IsAny<string>(), 0))
.Returns(new List<UnspentOutputReference>
{
new UnspentOutputReference
{
Address = new HdAddress
{
Address = senderAddress
},
Transaction = new TransactionData
{
Id = utxoId,
Index = utxoIndex,
}
}, new UnspentOutputReference
{
Address = new HdAddress
{
Address = senderAddress
},
Transaction = new TransactionData
{
Id = utxoIdUnused,
Index = utxoIndex,
}
}
});

var wallet = new Features.Wallet.Wallet();
wallet.AccountsRoot.Add(new AccountRoot(wallet));
var account0 = new HdAccount(wallet.AccountsRoot.First().Accounts) { Name = request.AccountName };
account0.ExternalAddresses.Add(new HdAddress() { Address = senderAddress });

this.walletManager.Setup(x => x.GetWallet(request.WalletName))
.Returns(wallet);

var reserveUtxoService = new ReserveUtxoService(this.loggerFactory, new Mock<ISignals>().Object);

var service = new SmartContractTransactionService(
this.network,
this.walletManager.Object,
this.walletTransactionHandler.Object,
this.stringSerializer.Object,
this.callDataSerializer.Object,
this.addressGenerator.Object,
this.stateRepository.Object,
reserveUtxoService);

BuildCallContractTransactionResponse result = service.BuildCallTx(request);

byte[] buffer = Convert.FromBase64String(sig1).Concat(Convert.FromBase64String(sig2)).ToArray();
string expected = $"{(int)MethodParameterDataType.ByteArray}#{Encoders.Hex.EncodeData(buffer)}";

this.stringSerializer.Verify(x => x.Deserialize(It.Is<string[]>(x => x[0] == expected)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ public BuildCallContractTransactionRequest()
/// </summary>
public string[] Parameters { get; set; }

/// <summary>
/// An array of base 64 encoded strings containing the signatures to add.
/// </summary>
/// <remarks>
/// The strings passed here are typically obtained by signatories via the "signmessage" API by signing the "challenge" string returned by a method.
/// </remarks>
public string[] Signatures { get; set; }

public override string ToString()
{
var builder = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ public BuildCreateContractTransactionRequest()
/// </summary>
public string[] Parameters { get; set; }

/// <summary>
/// An array of base 64 encoded strings containing the signatures to add.
/// </summary>
/// <remarks>
/// The strings passed here are typically obtained by signatories via the "signmessage" API by signing the "challenge" string returned by a method.
/// </remarks>

public string[] Signatures { get; set; }

public override string ToString()
{
var builder = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using CSharpFunctionalExtensions;
using NBitcoin;
using NBitcoin.DataEncoders;
using Stratis.Bitcoin.Features.SmartContracts.Models;
using Stratis.Bitcoin.Features.Wallet;
using Stratis.Bitcoin.Features.Wallet.Interfaces;
Expand Down Expand Up @@ -198,6 +199,41 @@ public BuildContractTransactionResult BuildTx(BuildContractTransactionRequest re
return BuildContractTransactionResult.Success(model);
}

private string[] ReplaceSignatures(string[] parameters, string[] signatures)
{
const int signatureLength = 65;
const int minHeaderByte = 27;
const int maxHeaderByte = 34;

if (parameters == null)
return null;

// Replace SIG# with any included signatures.
string encodedSigs = null;
for (int i = 0; i < parameters.Length; i++)
{
if (parameters[i].ToUpper() == "SIG#")
{
if (encodedSigs == null)
{
var sigs = (signatures ?? new string[0]).Select(s => Convert.FromBase64String(s)).ToArray();
if (sigs.Any(s => s.Length != signatureLength || s[0] < minHeaderByte || s[0] > maxHeaderByte))
throw new Exception("Invalid signature(s).");

var sigbuf = new byte[sigs.Length * signatureLength];
for (int j = 0; j < sigs.Length; j++)
Array.Copy(sigs[j], 0, sigbuf, j * signatureLength, signatureLength);

encodedSigs = $"{(int)MethodParameterDataType.ByteArray}#{Encoders.Hex.EncodeData(sigbuf)}";
}

parameters[i] = encodedSigs;
}
}

return parameters;
}

public BuildCallContractTransactionResponse BuildCallTx(BuildCallContractTransactionRequest request)
{
if (!this.CheckBalance(request.Sender))
Expand All @@ -214,6 +250,9 @@ public BuildCallContractTransactionResponse BuildCallTx(BuildCallContractTransac
{
try
{
// If signatures have been included then they replace the SIG# parameter.
request.Parameters = ReplaceSignatures(request.Parameters, request.Signatures);

object[] methodParameters = this.methodParameterStringSerializer.Deserialize(request.Parameters);
txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasPrice, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasLimit, addressNumeric, request.MethodName, methodParameters);
}
Expand Down Expand Up @@ -275,6 +314,9 @@ public BuildCreateContractTransactionResponse BuildCreateTx(BuildCreateContractT
{
try
{
// If signatures have been included then they replace the SIG# parameter.
request.Parameters = ReplaceSignatures(request.Parameters, request.Signatures);

object[] methodParameters = this.methodParameterStringSerializer.Deserialize(request.Parameters);
txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasPrice, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasLimit, request.ContractCode.HexToByteArray(), methodParameters);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NBitcoin;
using NBitcoin.DataEncoders;
using Stratis.Bitcoin.Connection;
using Stratis.Bitcoin.Features.SmartContracts.Models;
using Stratis.Bitcoin.Features.Wallet;
Expand Down