Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

Fix memo check to support muxed accounts #255

Merged
merged 2 commits into from
Apr 29, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 32 additions & 3 deletions stellar-dotnet-sdk-test/ServerCheckMemoRequiredTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,32 @@ public async Task TestSkipCheckIfHasMemo()
await _server.CheckMemoRequired(tx);
}

[TestMethod]
public async Task TestCheckFeeBumpTransaction()
{
var accountId = "GAYHAAKPAQLMGIJYMIWPDWCGUCQ5LAWY4Q7Q3IKSP57O7GUPD3NEOSEA";
var innerTx = BuildTransaction(accountId, new Operation[] { }, Memo.Text("foobar"));
var feeSource = KeyPair.FromAccountId("GD7HCWFO77E76G6BKJLRHRFRLE6I7BMPJQZQKGNYTT3SPE6BA4DHJAQY");
var tx = TransactionBuilder.BuildFeeBumpTransaction(feeSource, innerTx, 200);
await _server.CheckMemoRequired(tx);
}

[TestMethod]
public async Task TestSkipCheckIfDestinationIsMuxedAccount()
{
var accountId = "GAYHAAKPAQLMGIJYMIWPDWCGUCQ5LAWY4Q7Q3IKSP57O7GUPD3NEOSEA";

var muxed = MuxedAccountMed25519.FromMuxedAccountId(
"MAAAAAAAAAAAJURAAB2X52XFQP6FBXLGT6LWOOWMEXWHEWBDVRZ7V5WH34Y22MPFBHUHY");

var payment = new PaymentOperation
.Builder(muxed, new AssetTypeNative(), "100.500")
.Build();

var tx = BuildTransaction(accountId, new Operation[] { payment }, Memo.None(), skipDefaultOp: true);
await _server.CheckMemoRequired(tx);
}

private string BuildAccountResponse(string accountId, Dictionary<string, string> data = null)
{
var accountData = data ?? new Dictionary<string, string>();
Expand Down Expand Up @@ -184,15 +210,18 @@ private Transaction BuildTransaction(string destination)
return BuildTransaction(destination, new Operation[] { });
}

private Transaction BuildTransaction(string destinationAccountId, Operation[] operations, Memo memo = null)
private Transaction BuildTransaction(string destinationAccountId, Operation[] operations, Memo memo = null, bool skipDefaultOp = false)
{
var keypair = KeyPair.Random();
var destination = KeyPair.FromAccountId(destinationAccountId);
var account = new AccountResponse(destinationAccountId, 56199647068161);
var builder = new TransactionBuilder(account)
.AddOperation(
var builder = new TransactionBuilder(account);
if (!skipDefaultOp)
{
builder.AddOperation(
new PaymentOperation.Builder(destination, new AssetTypeNative(), "100.50")
.Build());
}

if (memo != null)
{
Expand Down
1 change: 1 addition & 0 deletions stellar-dotnet-sdk/IAccountId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ public interface IAccountId
byte[] PublicKey { get; }
string Address { get; }
string AccountId { get; }
bool IsMuxedAccount { get; }
}
}
6 changes: 3 additions & 3 deletions stellar-dotnet-sdk/InflationOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace stellar_dotnet_sdk
{
/// <summary>
/// Use <see cref="Builder"/> to create a new InflationOperation.
///
///
/// See also: <see href="https://www.stellar.org/developers/guides/concepts/list-of-operations.html#inflation">Inflation</see>
/// </summary>
public class InflationOperation : Operation
Expand All @@ -22,14 +22,14 @@ public override xdr.Operation.OperationBody ToOperationBody()

public class Builder
{
private KeyPair mSourceAccount;
private IAccountId mSourceAccount;

/// <summary>
/// Sets the source account for this operation.
/// </summary>
/// <param name="sourceAccount">The operation's source account.</param>
/// <returns>Builder object so you can chain methods.</returns>
public Builder SetSourceAccount(KeyPair sourceAccount)
public Builder SetSourceAccount(IAccountId sourceAccount)
{
mSourceAccount = sourceAccount ?? throw new ArgumentNullException(nameof(sourceAccount), "sourceAccount cannot be null");
return this;
Expand Down
2 changes: 2 additions & 0 deletions stellar-dotnet-sdk/KeyPair.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ public xdr.MuxedAccount MuxedAccount
}
}

public bool IsMuxedAccount => false;

/// <summary>
/// Returns a KeyPair from a Public Key
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions stellar-dotnet-sdk/MuxedAccountMed25519.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,7 @@ public xdr.MuxedAccount MuxedAccount
/// Get the MuxedAccount account id, starting with M.
/// </summary>
public string AccountId => Address;

public bool IsMuxedAccount => true;
}
}
4 changes: 2 additions & 2 deletions stellar-dotnet-sdk/PathPaymentStrictReceiveOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public Builder(sdkxdr.PathPaymentStrictReceiveOp op)
/// <param name="destAsset"> The asset the destination account receives.</param>
/// <param name="destAmount"> The amount of destination asset the destination account receives.</param>
/// <exception cref="ArithmeticException"> When sendMax or destAmount has more than 7 decimal places.</exception>
public Builder(Asset sendAsset, string sendMax, KeyPair destination, Asset destAsset, string destAmount)
public Builder(Asset sendAsset, string sendMax, IAccountId destination, Asset destAsset, string destAmount)
{
_SendAsset = sendAsset ?? throw new ArgumentNullException(nameof(sendAsset), "sendAsset cannot be null");
_SendMax = sendMax ?? throw new ArgumentNullException(nameof(sendMax), "sendMax cannot be null");
Expand Down Expand Up @@ -143,7 +143,7 @@ public Builder SetPath(Asset[] path)
/// </summary>
/// <param name="sourceAccount"> The operation's source account.</param>
/// <returns>Builder object so you can chain methods.</returns>
public Builder SetSourceAccount(KeyPair sourceAccount)
public Builder SetSourceAccount(IAccountId sourceAccount)
{
_SourceAccount = sourceAccount ?? throw new ArgumentNullException(nameof(sourceAccount), "sourceAccount cannot be null");
return this;
Expand Down
4 changes: 2 additions & 2 deletions stellar-dotnet-sdk/PathPaymentStrictSendOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public Builder(sdkxdr.PathPaymentStrictSendOp op)
/// <param name="destAsset"> The asset the destination account receives.</param>
/// <param name="destMin"> The amount of destination asset the destination account receives.</param>
/// <exception cref="ArithmeticException"> When sendAmount or destMin has more than 7 decimal places.</exception>
public Builder(Asset sendAsset, string sendAmount, KeyPair destination, Asset destAsset, string destMin)
public Builder(Asset sendAsset, string sendAmount, IAccountId destination, Asset destAsset, string destMin)
{
_SendAsset = sendAsset ?? throw new ArgumentNullException(nameof(sendAsset), "sendAsset cannot be null");
_SendAmount = sendAmount ?? throw new ArgumentNullException(nameof(sendAmount), "sendAmount cannot be null");
Expand Down Expand Up @@ -144,7 +144,7 @@ public Builder SetPath(Asset[] path)
/// </summary>
/// <param name="sourceAccount"> The operation's source account.</param>
/// <returns>Builder object so you can chain methods.</returns>
public Builder SetSourceAccount(KeyPair sourceAccount)
public Builder SetSourceAccount(IAccountId sourceAccount)
{
_SourceAccount = sourceAccount ?? throw new ArgumentNullException(nameof(sourceAccount), "sourceAccount cannot be null");
return this;
Expand Down
35 changes: 18 additions & 17 deletions stellar-dotnet-sdk/PaymentOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,24 +56,25 @@ public override xdr.Operation.OperationBody ToOperationBody()
///<summary>
/// Builds Payment operation.
///</summary>
///<see cref="PathPaymentOperation"/>
///<see cref="PathPaymentStrictReceiveOperation"/>
/// ///<see cref="PathPaymentStrictSendOperation"/>
public class Builder
{
private readonly string amount;
private readonly Asset asset;
private readonly IAccountId destination;
private readonly string _amount;
private readonly Asset _asset;
private readonly IAccountId _destination;

private IAccountId mSourceAccount;
private IAccountId _sourceAccount;

///<summary>
/// Construct a new PaymentOperation builder from a PaymentOp XDR.
///</summary>
///<param name="op"><see cref="PaymentOp"/></param>
public Builder(PaymentOp op)
{
destination = MuxedAccount.FromXdrMuxedAccount(op.Destination);
asset = Asset.FromXdr(op.Asset);
amount = FromXdrAmount(op.Amount.InnerValue);
_destination = MuxedAccount.FromXdrMuxedAccount(op.Destination);
_asset = Asset.FromXdr(op.Asset);
_amount = FromXdrAmount(op.Amount.InnerValue);
}

///<summary>
Expand All @@ -82,11 +83,11 @@ public Builder(PaymentOp op)
///<param name="destination">The destination keypair (uses only the public key).</param>
///<param name="asset">The asset to send.</param>
///<param name="amount">The amount to send in lumens.</param>
public Builder(KeyPair destination, Asset asset, string amount)
public Builder(IAccountId destination, Asset asset, string amount)
{
this.destination = destination;
this.asset = asset;
this.amount = amount;
_destination = destination;
_asset = asset;
_amount = amount;
}

///<summary>
Expand All @@ -95,9 +96,9 @@ public Builder(KeyPair destination, Asset asset, string amount)
///<param name="account">The operation's source account.</param>
///<returns>Builder object so you can chain methods.</returns>
///
public Builder SetSourceAccount(KeyPair account)
public Builder SetSourceAccount(IAccountId account)
{
mSourceAccount = account;
_sourceAccount = account;
return this;
}

Expand All @@ -106,9 +107,9 @@ public Builder SetSourceAccount(KeyPair account)
///</summary>
public PaymentOperation Build()
{
var operation = new PaymentOperation(destination, asset, amount);
if (mSourceAccount != null)
operation.SourceAccount = mSourceAccount;
var operation = new PaymentOperation(_destination, _asset, _amount);
if (_sourceAccount != null)
operation.SourceAccount = _sourceAccount;
return operation;
}
}
Expand Down
39 changes: 31 additions & 8 deletions stellar-dotnet-sdk/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,13 @@ public async Task<SubmitTransactionResponse> SubmitTransaction(string transactio
/// It will sequantially load each destination account and check if it has the data field
/// <c>config.memo_required</c> set to <c>"MQ=="</c>.
/// </summary>
/// <param name="tx"></param>
/// <param name="transaction"></param>
/// <returns></returns>
/// <exception cref="AccountRequiresMemoException"></exception>
public async Task CheckMemoRequired(Transaction tx)
public async Task CheckMemoRequired(TransactionBase transaction)
{
var tx = GetTransactionToCheck(transaction);

if (tx.Memo != null && !Equals(tx.Memo, Memo.None()))
{
return;
Expand All @@ -185,7 +187,15 @@ public async Task CheckMemoRequired(Transaction tx)
continue;
}

var destination = PaymentOperationDestination(operation);
// If it's a muxed account it already contains the memo.
var destinationKey = PaymentOperationDestination(operation);
if (destinationKey.IsMuxedAccount)
{
continue;
}

var destination = destinationKey.Address;

if (destinations.Contains(destination))
{
continue;
Expand Down Expand Up @@ -228,6 +238,19 @@ public static HttpClient CreateHttpClient(HttpMessageHandler handler)
return httpClient;
}

private Transaction GetTransactionToCheck(TransactionBase transaction)
{
switch (transaction)
{
case FeeBumpTransaction feeBump:
return feeBump.InnerTransaction;
case Transaction tx:
return tx;
default:
throw new ArgumentException($"Invalid transaction of type {transaction.GetType().Name}");
}
}

private bool IsPaymentOperation(Operation op)
{
switch (op)
Expand All @@ -242,18 +265,18 @@ private bool IsPaymentOperation(Operation op)
}
}

private string PaymentOperationDestination(Operation op)
private IAccountId PaymentOperationDestination(Operation op)
{
switch (op)
{
case PaymentOperation p:
return p.Destination.Address;
return p.Destination;
case PathPaymentStrictSendOperation p:
return p.Destination.Address;
return p.Destination;
case PathPaymentStrictReceiveOperation p:
return p.Destination.Address;
return p.Destination;
case AccountMergeOperation p:
return p.Destination.Address;
return p.Destination;
default:
throw new ArgumentException("Expected payment operation.");
}
Expand Down