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

Commit

Permalink
Merge pull request #255 from elucidsoft/fix-memo-check
Browse files Browse the repository at this point in the history
Fix memo check to support muxed accounts
  • Loading branch information
elucidsoft committed Apr 29, 2020
2 parents 80a95ba + db8735c commit 8f53a42
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 35 deletions.
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

0 comments on commit 8f53a42

Please sign in to comment.