Skip to content

Commit

Permalink
Merge branch 'main' into oliver
Browse files Browse the repository at this point in the history
  • Loading branch information
heliuchuan authored Oct 20, 2024
2 parents c951f4d + bbbd0c6 commit 11a8186
Show file tree
Hide file tree
Showing 15 changed files with 127 additions and 51 deletions.
2 changes: 1 addition & 1 deletion Aptos.Tests/Aptos.Clients/Aptos.AccountClient.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ await client.Account.GetCoinBalance(
Assert.Null(
await client.Account.GetCoinBalance(
"0x66cb05df2d855fbae92cdb2dfac9a0b29c969a03998fa817735d27391b52b189",
"0x123a"
"0xc"
)
);
}
Expand Down
6 changes: 3 additions & 3 deletions Aptos.Tests/Aptos.Core/AccountAddress.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ public void WhenIConvertTheTypeToAType(string _, string outputType)
{
_output = outputType switch
{
"string" => AccountAddress.From(_inputValue),
"string long" => AccountAddress.From(_inputValue).ToStringLong(),
"string" => AccountAddress.From(_inputValue, 63),
"string long" => AccountAddress.From(_inputValue, 63).ToStringLong(),
_ => throw new ArgumentException("Invalid type"),
};
}
Expand All @@ -50,7 +50,7 @@ public void ThenTheResultShouldBeTypeValue(string type, string value)
switch (type)
{
case "address":
Assert.Equal(AccountAddress.From(value), _output);
Assert.Equal(AccountAddress.From(value, 63), _output);
break;
case "string":
Assert.Equal(value.Trim('\"'), _output.ToString());
Expand Down
13 changes: 8 additions & 5 deletions Aptos.Tests/Aptos.Crypto/MultiKey.Tests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
namespace Aptos.Tests.Crypto;

using Aptos.Indexer.GraphQL;
using Newtonsoft.Json;
using Xunit.Gherkin.Quick;

[FeatureFile("../../../../features/multi_key.feature")]
Expand Down Expand Up @@ -45,9 +43,10 @@ public void GivenValue(string type, string values)
var signerTypes = splitValues[1].Split(",");
var signers = splitValues[2].Split(",");
List<Account> deserializedSigners = signerTypes
.Select<string, Account>(
.Select(
(type, i) =>
type switch
{
Account account = type switch
{
"ed25519_ed25519_pk" => new Ed25519Account(
Ed25519PrivateKey.Deserialize(new(signers[i]))
Expand All @@ -57,7 +56,11 @@ public void GivenValue(string type, string values)
),
"account_keyless" => KeylessAccount.Deserialize(new(signers[i])),
_ => throw new ArgumentException("Invalid signer type"),
}
};
if (account is KeylessAccount keylessAccount)
BaseTests.MockKeylessAccount(keylessAccount);
return account;
}
)
.ToList();
_inputValue = new MultiKeyAccount(MultiKey.Deserialize(new(key)), deserializedSigners);
Expand Down
2 changes: 2 additions & 0 deletions Aptos.Tests/Aptos.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
<IsTestProject>true</IsTestProject>
</PropertyGroup>


<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="Xunit.Gherkin.Quick" Version="4.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
Expand Down
17 changes: 17 additions & 0 deletions Aptos.Tests/BaseTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Reflection;
using dotenv.net;
using Moq;

namespace Aptos.Tests;

Expand Down Expand Up @@ -30,4 +32,19 @@ public static string[] ParseArray(string input)
// Split the input string by commas and convert to integers
return [.. input.Split(',')];
}

/// <summary>
/// Mock a KeylessAccount to enable signing even if the EphemeralKeyPair is expired.
/// </summary>
/// <param name="keylessAccount">The keyless account with an expired values.</param>
public static void MockKeylessAccount(KeylessAccount keylessAccount)
{
var field = typeof(KeylessAccount).GetField(
"EphemeralKeyPair",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public
);
var mock = new Mock<EphemeralKeyPair>(keylessAccount.EphemeralKeyPair);
mock.Setup(m => m.IsExpired()).Returns(false);
field?.SetValue(keylessAccount, mock.Object);
}
}
7 changes: 5 additions & 2 deletions Aptos/Aptos.Accounts/EphemeralKeyPair.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class EphemeralKeyPair : Serializable
/// The private key is used to sign transactions. This private key is not tied to any account on the chain as it is
/// ephemeral (not permanent) in nature.
/// </summary>
private PrivateKey _privateKey;
private readonly PrivateKey _privateKey;

/// <summary>
/// A public key used to verify transactions. This public key is not tied to any account on the chain as it is
Expand All @@ -39,6 +39,9 @@ public class EphemeralKeyPair : Serializable
/// </summary>
public readonly string Nonce;

public EphemeralKeyPair(EphemeralKeyPair other)
: this(other._privateKey, other.ExpiryTimestamp, other.Blinder) { }

public EphemeralKeyPair(
PrivateKey privateKey,
ulong? expiryTimestamp = null,
Expand All @@ -64,7 +67,7 @@ public EphemeralKeyPair(
Nonce = Hash.PoseidonHash(fields).ToString();
}

public bool IsExpired() => (ulong)DateTime.Now.ToUnixTimestamp() > ExpiryTimestamp;
public virtual bool IsExpired() => (ulong)DateTime.Now.ToUnixTimestamp() > ExpiryTimestamp;

public EphemeralSignature Sign(byte[] data)
{
Expand Down
2 changes: 0 additions & 2 deletions Aptos/Aptos.Accounts/KeylessAccount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ public override Signature Sign(AnyRawTransaction transaction)

public override Signature Sign(byte[] message)
{
if (EphemeralKeyPair.IsExpired())
throw new Exception("Ephemeral keypair has expired");
var token = new JsonWebToken(Jwt);
return new KeylessSignature(
ephemeralCertificate: new EphemeralCertificate(
Expand Down
28 changes: 23 additions & 5 deletions Aptos/Aptos.Core/AccountAddress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public override void SerializeForScriptFunction(Serializer s)

public static AccountAddress Deserialize(Deserializer d) => new(d.FixedBytes(LENGTH));

public static AccountAddress FromString(string str)
public static AccountAddress FromString(string str, int maxMissingChars = 4)
{
string parsedInput = str;

Expand All @@ -114,10 +114,16 @@ public static AccountAddress FromString(string str)
AccountAddressInvalidReason.TooLong
);

byte[] addressBytes;
if (maxMissingChars > 63 || maxMissingChars < 0)
throw new AccountAddressParsingException(
$"maxMissingChars must be between or equal to 0 and 63. Received {maxMissingChars}",
AccountAddressInvalidReason.InvalidPaddingStrictness
);

AccountAddress address;
try
{
addressBytes = Utilities.HexStringToBytes(parsedInput.PadLeft(64, '0'));
address = new AccountAddress(Utilities.HexStringToBytes(parsedInput.PadLeft(64, '0')));
}
catch
{
Expand All @@ -127,7 +133,18 @@ public static AccountAddress FromString(string str)
);
}

return new AccountAddress(addressBytes);
if (parsedInput.Length < 64 - maxMissingChars)
{
if (!address.IsSpecial())
{
throw new AccountAddressParsingException(
$"Hex string is too short, must be between {64 - maxMissingChars} and 64 chars, excluding the leading 0x. You may need to fix the address by pading it with 0s before passing it to `fromString` (e.g. <addressString>.PadLeft(64, '0')). Received {str}",
AccountAddressInvalidReason.TooShort
);
}
}

return address;
}

public static AccountAddress FromStringStrict(string str)
Expand Down Expand Up @@ -161,7 +178,8 @@ public static AccountAddress FromStringStrict(string str)
return address;
}

public static AccountAddress From(string str) => FromString(str);
public static AccountAddress From(string str, int maxMissingChars = 4) =>
FromString(str, maxMissingChars);

public static AccountAddress From(byte[] bytes) => new(bytes);

Expand Down
14 changes: 14 additions & 0 deletions Aptos/Aptos.Crypto/MultiKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ public static int BitCount(byte b)
n = (n + (n >> 4)) & 0x0F0F0F0Fu;
return (int)((n * 0x01010101u) >> 24);
}

public static MultiKeySignature GetSimulationSignature(MultiKey multiKey) =>
GetSimulationSignature(multiKey.PublicKeys.Count);

public static MultiKeySignature GetSimulationSignature(int keysCount)
{
return new MultiKeySignature(
signatures: Enumerable
.Range(0, keysCount)
.Select(i => (PublicKeySignature)new Ed25519Signature(new byte[64]))
.ToList(),
bitmap: CreateBitmap(Enumerable.Range(0, keysCount).Select(i => i).ToArray())
);
}
}

public partial class MultiKey : Serializable, IVerifyingKey
Expand Down
1 change: 1 addition & 0 deletions Aptos/Aptos.Exceptions/Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public enum AccountAddressInvalidReason
LeadingZeroXRequired,
LongFormRequiredUnlessSpecial,
InvalidPaddingZeroes,
InvalidPaddingStrictness,
}

public class AccountAddressParsingException(string message, AccountAddressInvalidReason reason)
Expand Down
15 changes: 9 additions & 6 deletions Aptos/Aptos.Transactions/SimulateTransactionData.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using OneOf;

namespace Aptos;

public class SimulateTransactionOptions(
Expand All @@ -13,15 +15,16 @@ public class SimulateTransactionOptions(

public class SimulateTransactionData(
AnyRawTransaction transaction,
PublicKey signerPublicKey,
PublicKey[]? secondarySignersPublicKeys = null,
PublicKey? feePayerPublicKey = null,
OneOf<PublicKey, IVerifyingKey> signerPublicKey,
OneOf<PublicKey, IVerifyingKey>[]? secondarySignersPublicKeys = null,
OneOf<PublicKey, IVerifyingKey>? feePayerPublicKey = null,
SimulateTransactionOptions? options = null
)
{
public AnyRawTransaction Transaction = transaction;
public PublicKey SignerPublicKey = signerPublicKey;
public PublicKey[]? SecondarySignersPublicKeys = secondarySignersPublicKeys;
public PublicKey? FeePayerPublicKey = feePayerPublicKey;
public OneOf<PublicKey, IVerifyingKey> SignerPublicKey = signerPublicKey;
public OneOf<PublicKey, IVerifyingKey>[]? SecondarySignersPublicKeys =
secondarySignersPublicKeys;
public OneOf<PublicKey, IVerifyingKey>? FeePayerPublicKey = feePayerPublicKey;
public SimulateTransactionOptions? Options = options;
}
60 changes: 38 additions & 22 deletions Aptos/Aptos.Transactions/TransactionBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,38 +1,54 @@
namespace Aptos;

using Aptos.Core;
using Aptos.Exceptions;
using OneOf;

public static class TransactionBuilder
{
#region GetAuthenticator

public static AccountAuthenticator GetAuthenticatorForSimulation(PublicKey publicKey)
public static AccountAuthenticator GetAuthenticatorForSimulation(
OneOf<PublicKey, IVerifyingKey> publicOrVerifyingKey
)
{
Ed25519Signature invalidSignature = new(new byte[64]);

if (publicKey is Ed25519PublicKey ed25519PublicKey)
{
return new AccountAuthenticatorEd25519(ed25519PublicKey, invalidSignature);
}

if (publicKey is KeylessPublicKey keylessPublicKey)
{
return new AccountAuthenticatorSingleKey(
keylessPublicKey,
Keyless.GetSimulationSignature()
);
}
return publicOrVerifyingKey.Match<AccountAuthenticator>(
publicKey =>
{
if (publicKey is Ed25519PublicKey ed25519PublicKey)
return new AccountAuthenticatorEd25519(ed25519PublicKey, invalidSignature);
if (publicKey is KeylessPublicKey keylessPublicKey)
return new AccountAuthenticatorSingleKey(
keylessPublicKey,
Keyless.GetSimulationSignature()
);
if (publicKey is FederatedKeylessPublicKey federatedKeylessPublicKey)
return new AccountAuthenticatorSingleKey(
federatedKeylessPublicKey,
Keyless.GetSimulationSignature()
);
return new AccountAuthenticatorSingleKey(publicKey, invalidSignature);
},
verifyingKey =>
{
if (verifyingKey is SingleKey singleKey)
return new AccountAuthenticatorSingleKey(singleKey.PublicKey, invalidSignature);
if (publicKey is FederatedKeylessPublicKey federatedKeylessPublicKey)
{
return new AccountAuthenticatorSingleKey(
federatedKeylessPublicKey,
Keyless.GetSimulationSignature()
);
}
if (verifyingKey is MultiKey multiKey)
return new AccountAuthenticatorMultiKey(
multiKey,
MultiKey.GetSimulationSignature(multiKey)
);
return new AccountAuthenticatorSingleKey(publicKey, invalidSignature);

Check failure on line 46 in Aptos/Aptos.Transactions/TransactionBuilder.cs

View workflow job for this annotation

GitHub Actions / Run Unit Tests

The name 'publicKey' does not exist in the current context

Check failure on line 46 in Aptos/Aptos.Transactions/TransactionBuilder.cs

View workflow job for this annotation

GitHub Actions / Run Unit Tests

The name 'publicKey' does not exist in the current context

Check failure on line 46 in Aptos/Aptos.Transactions/TransactionBuilder.cs

View workflow job for this annotation

GitHub Actions / Run Unit Tests

The name 'publicKey' does not exist in the current context

Check failure on line 46 in Aptos/Aptos.Transactions/TransactionBuilder.cs

View workflow job for this annotation

GitHub Actions / Run Unit Tests

The name 'publicKey' does not exist in the current context

Check failure on line 46 in Aptos/Aptos.Transactions/TransactionBuilder.cs

View workflow job for this annotation

GitHub Actions / Run Unit Tests

The name 'publicKey' does not exist in the current context

Check failure on line 46 in Aptos/Aptos.Transactions/TransactionBuilder.cs

View workflow job for this annotation

GitHub Actions / Run Unit Tests

The name 'publicKey' does not exist in the current context

Check failure on line 46 in Aptos/Aptos.Transactions/TransactionBuilder.cs

View workflow job for this annotation

GitHub Actions / Run Unit Tests

The name 'publicKey' does not exist in the current context

Check failure on line 46 in Aptos/Aptos.Transactions/TransactionBuilder.cs

View workflow job for this annotation

GitHub Actions / Run Unit Tests

The name 'publicKey' does not exist in the current context
throw new ArgumentException(
$"{verifyingKey.GetType().Name} is not supported for simulation."
);
}
);
}

#endregion
Expand Down Expand Up @@ -124,7 +140,7 @@ SimulateTransactionData data
?? [];

AccountAuthenticator feePayerAuthenticator = GetAuthenticatorForSimulation(
data.FeePayerPublicKey
(OneOf<PublicKey, IVerifyingKey>)data.FeePayerPublicKey
);

TransactionAuthenticatorFeePayer feePayerTransactionAuthenticator =
Expand Down
1 change: 1 addition & 0 deletions Aptos/Aptos.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
<PackageReference Include="OneOf" Version="3.0.271" />
<PackageReference Include="StrawberryShake.Transport.Http" Version="13.9.11" />
<PackageReference Include="StrawberryShake" Version="13.9.11" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- There should only be one <DefaultVersion> in the file. If moved, it should be updated in the GitHub Actions workflow. -->
<DefaultVersion>0.0.9</DefaultVersion>
<DefaultVersion>0.0.11</DefaultVersion>
<DefaultTargetFrameworks>net8.0;net7.0;net6.0;netstandard2.1</DefaultTargetFrameworks>
<DefaultTestingFrameworks>net8.0</DefaultTestingFrameworks>
</PropertyGroup>
Expand Down
8 changes: 4 additions & 4 deletions index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dotnet add package Aptos

## Explore

- [Aptos](/docs/Aptos.html): The main namespace for interacting with the Aptos blockchain.
- [Aptos.Core](/docs/Aptos.Core.html): Core utilities for the SDK.
- [Aptos.Schemes](/docs/Aptos.Schemes.html): All authentication schemes supported in the SDK.
- [Aptos.Exceptions](/docs/Aptos.Exceptions.html): All data types for exceptions thrown by the SDK.
- [Aptos](/aptos-dotnet-sdk/docs/Aptos.html): The main namespace for interacting with the Aptos blockchain.
- [Aptos.Core](/aptos-dotnet-sdk/docs/Aptos.Core.html): Core utilities for the SDK.
- [Aptos.Schemes](/aptos-dotnet-sdk/docs/Aptos.Schemes.html): All authentication schemes supported in the SDK.
- [Aptos.Exceptions](/aptos-dotnet-sdk/docs/Aptos.Exceptions.html): All data types for exceptions thrown by the SDK.

0 comments on commit 11a8186

Please sign in to comment.