diff --git a/Aptos.Examples/Main.cs b/Aptos.Examples/Main.cs
index 3071db8..cc34e16 100644
--- a/Aptos.Examples/Main.cs
+++ b/Aptos.Examples/Main.cs
@@ -7,7 +7,9 @@ public class RunExample
{
{ "1", KeylessTransferExample.Run },
{ "2", SimpleTransferExample.Run },
- { "3", PlaygroundExample.Run },
+ { "3", SimpleTransferSingleKeyExample.Run },
+ { "4", SimpleTransferMultiKeyExample.Run },
+ { "5", PlaygroundExample.Run },
};
public static async Task Main()
diff --git a/Aptos.Examples/SimpleTransferMultiKey.cs b/Aptos.Examples/SimpleTransferMultiKey.cs
new file mode 100644
index 0000000..5713e2c
--- /dev/null
+++ b/Aptos.Examples/SimpleTransferMultiKey.cs
@@ -0,0 +1,50 @@
+using Newtonsoft.Json;
+
+namespace Aptos.Examples;
+
+public class SimpleTransferMultiKeyExample
+{
+ public static async Task Run()
+ {
+ Console.WriteLine("=== Addresses ===\n");
+ var aptos = new AptosClient(new AptosConfig(Networks.Devnet));
+ var aliceSigner = Account.Generate();
+ var bobSigner = SingleKeyAccount.Generate();
+ var account = new MultiKeyAccount(
+ new([aliceSigner.PublicKey, bobSigner.PublicKey], 1),
+ [aliceSigner]
+ );
+ Console.WriteLine($"Alice-Bob MultiKey (1/2): {account.Address}");
+
+ Console.WriteLine("\n=== Funding accounts ===\n");
+ var aliceFundTxn = await aptos.Faucet.FundAccount(account.Address, 100_000_000);
+ Console.WriteLine($"Alice-Bob MultiKey (1/2)'s fund transaction: {aliceFundTxn.Hash}");
+
+ Console.WriteLine("\n=== Building transaction ===\n");
+
+ var txn = await aptos.Transaction.Build(
+ sender: account.Address,
+ data: new GenerateEntryFunctionPayloadData(
+ function: "0x1::coin::transfer",
+ typeArguments: ["0x1::aptos_coin::AptosCoin"],
+ functionArguments: [account.Address, "100000"]
+ )
+ );
+ Console.WriteLine($"{JsonConvert.SerializeObject(txn.RawTransaction)}");
+
+ Console.WriteLine("\n=== Signing and submitting transaction ===\n");
+
+ var pendingTxn = await aptos.Transaction.SignAndSubmitTransaction(account, txn);
+ Console.WriteLine($"Submitted transaction with hash: {pendingTxn.Hash}");
+
+ Console.WriteLine("Waiting for transaction...");
+ var committedTxn = await aptos.Transaction.WaitForTransaction(pendingTxn.Hash.ToString());
+ Console.WriteLine(
+ $"Transaction {committedTxn.Hash} is {(committedTxn.Success ? "success" : "failure")}"
+ );
+
+ Console.WriteLine("\n=== Account Balance ===\n");
+ var balance = await aptos.Account.GetCoinBalance(account.Address, "0xa");
+ Console.WriteLine($"Account {account.Address} has {balance?.Amount ?? 0} coin balances");
+ }
+}
diff --git a/Aptos.Examples/SimpleTransferSingleKey.cs b/Aptos.Examples/SimpleTransferSingleKey.cs
new file mode 100644
index 0000000..4bad861
--- /dev/null
+++ b/Aptos.Examples/SimpleTransferSingleKey.cs
@@ -0,0 +1,45 @@
+using Newtonsoft.Json;
+
+namespace Aptos.Examples;
+
+public class SimpleTransferSingleKeyExample
+{
+ public static async Task Run()
+ {
+ Console.WriteLine("=== Addresses ===\n");
+ var aptos = new AptosClient(new AptosConfig(Networks.Devnet));
+ var account = SingleKeyAccount.Generate();
+ Console.WriteLine($"Alice: {account.Address}");
+
+ Console.WriteLine("\n=== Funding accounts ===\n");
+ var aliceFundTxn = await aptos.Faucet.FundAccount(account.Address, 100_000_000);
+ Console.WriteLine($"Alice's fund transaction: {aliceFundTxn.Hash}");
+
+ Console.WriteLine("\n=== Building transaction ===\n");
+
+ var txn = await aptos.Transaction.Build(
+ sender: account.Address,
+ data: new GenerateEntryFunctionPayloadData(
+ function: "0x1::coin::transfer",
+ typeArguments: ["0x1::aptos_coin::AptosCoin"],
+ functionArguments: [account.Address, "100000"]
+ )
+ );
+ Console.WriteLine($"{JsonConvert.SerializeObject(txn.RawTransaction)}");
+
+ Console.WriteLine("\n=== Signing and submitting transaction ===\n");
+
+ var pendingTxn = await aptos.Transaction.SignAndSubmitTransaction(account, txn);
+ Console.WriteLine($"Submitted transaction with hash: {pendingTxn.Hash}");
+
+ Console.WriteLine("Waiting for transaction...");
+ var committedTxn = await aptos.Transaction.WaitForTransaction(pendingTxn.Hash.ToString());
+ Console.WriteLine(
+ $"Transaction {committedTxn.Hash} is {(committedTxn.Success ? "success" : "failure")}"
+ );
+
+ Console.WriteLine("\n=== Account Balance ===\n");
+ var balance = await aptos.Account.GetCoinBalance(account.Address, "0xa");
+ Console.WriteLine($"Account {account.Address} has {balance?.Amount ?? 0} coin balances");
+ }
+}
diff --git a/Aptos.Tests/Aptos.Crypto/SingleKey.Tests.cs b/Aptos.Tests/Aptos.Crypto/SingleKey.Tests.cs
new file mode 100644
index 0000000..1affe92
--- /dev/null
+++ b/Aptos.Tests/Aptos.Crypto/SingleKey.Tests.cs
@@ -0,0 +1,81 @@
+namespace Aptos.Tests.Core;
+
+using Xunit.Gherkin.Quick;
+
+[FeatureFile("../../../../features/single_key.feature")]
+public sealed class SingleKeyFeatureTests : Feature
+{
+ private string? _publicKeyType;
+
+ private dynamic? _inputValue;
+
+ private dynamic? _output;
+
+ [Given(@"(ed25519|secp256k1|keyless) (.*)")]
+ public void GivenValue(string type, string value)
+ {
+ PublicKey publicKey = type switch
+ {
+ "ed25519" => Ed25519PublicKey.Deserialize(new(value)),
+ "secp256k1" => Secp256k1PublicKey.Deserialize(new(value)),
+ "keyless" => KeylessPublicKey.Deserialize(new(value)),
+ _ => throw new ArgumentException("Invalid public key type"),
+ };
+ _publicKeyType = type;
+ _inputValue = new SingleKey(publicKey);
+ }
+
+ [When(@"I serialize")]
+ public void WhenISerialize()
+ {
+ if (_inputValue == null)
+ throw new ArgumentException("No input value");
+ if (_inputValue is SingleKey singleKey)
+ _output = singleKey.BcsToHex();
+ }
+
+ [When(@"I derive authentication key")]
+ public void WhenIDeriveAuthenticationKey()
+ {
+ if (_inputValue == null)
+ throw new ArgumentException("No input value");
+ if (_inputValue is SingleKey singleKey)
+ _output = singleKey.AuthKey().BcsToHex();
+ }
+
+ [When(@"I verify signature (.*) with message (.*)")]
+ public void WhenIVerifySignatureWithMessage(string signature, string message)
+ {
+ if (_inputValue == null)
+ throw new ArgumentException("No input value");
+
+ if (_inputValue is SingleKey singleKey)
+ {
+ PublicKeySignature publicKeySignature = _publicKeyType switch
+ {
+ "ed25519" => Ed25519Signature.Deserialize(new(signature)),
+ "secp256k1" => Secp256k1Signature.Deserialize(new(signature)),
+ "keyless" => KeylessSignature.Deserialize(new(signature)),
+ _ => throw new ArgumentException("Invalid signature public key type"),
+ };
+ _output = singleKey.VerifySignature(message, publicKeySignature);
+ }
+ }
+
+ [Then(@"the result should be (.*) (.*)")]
+ public void ThenTheResultShouldBeTypeValue(string type, string value)
+ {
+ if (_output == null)
+ throw new ArgumentException("No output value");
+
+ switch (type)
+ {
+ case "bool":
+ Assert.Equal(bool.Parse(value), _output);
+ break;
+ case "bcs":
+ Assert.Equal(Hex.FromHexString(value), _output);
+ break;
+ }
+ }
+}
diff --git a/Aptos/Aptos.Accounts/Account.cs b/Aptos/Aptos.Accounts/Account.cs
index a342c4e..7c5f851 100644
--- a/Aptos/Aptos.Accounts/Account.cs
+++ b/Aptos/Aptos.Accounts/Account.cs
@@ -1,7 +1,7 @@
-namespace Aptos;
-
using Aptos.Schemes;
+namespace Aptos;
+
///
/// Abstract class representing a signer account.
///
@@ -10,7 +10,7 @@ public abstract class Account
///
/// Gets the public key of the account.
///
- public abstract AccountPublicKey PublicKey { get; }
+ public abstract IVerifyingKey VerifyingKey { get; }
///
/// Gets the address of the account.
@@ -25,6 +25,14 @@ public abstract class Account
///
public Signature Sign(string message) => Sign(SigningMessage.Convert(message));
+ ///
+ /// Signs a transaction using the account's private key.
+ ///
+ /// The transaction to sign.
+ /// The signed transaction.
+ public virtual Signature Sign(AnyRawTransaction transaction) =>
+ Sign(SigningMessage.GenerateForTransaction(transaction));
+
///
/// Signs a message with the using the signer.
///
@@ -32,32 +40,24 @@ public abstract class Account
/// The signed message.
public abstract Signature Sign(byte[] message);
- ///
- /// Signs a transaction using the account's private key.
- ///
- /// The transaction to sign.
- /// The signature of the transaction.
- public abstract Signature SignTransaction(AnyRawTransaction transaction);
-
///
public AccountAuthenticator SignWithAuthenticator(string message) =>
SignWithAuthenticator(SigningMessage.Convert(message));
///
- /// Signs a message and returns an authenticator for the account.
+ /// Signs a transaction and returns an authenticator for the account.
///
- /// The message to sign as a byte array.
+ /// The transaction to sign.
/// The authenticator containing the signature.
- public abstract AccountAuthenticator SignWithAuthenticator(byte[] message);
+ public virtual AccountAuthenticator SignWithAuthenticator(AnyRawTransaction transaction) =>
+ SignWithAuthenticator(SigningMessage.GenerateForTransaction(transaction));
///
- /// Signs a transaction and returns an authenticator for the account.
+ /// Signs a message and returns an authenticator for the account.
///
- /// The transaction to sign.
+ /// The message to sign as a byte array.
/// The authenticator containing the signature.
- public abstract AccountAuthenticator SignTransactionWithAuthenticator(
- AnyRawTransaction transaction
- );
+ public abstract AccountAuthenticator SignWithAuthenticator(byte[] message);
///
/// Generates a new Ed25519 account.
diff --git a/Aptos/Aptos.Accounts/Ed25519Account.cs b/Aptos/Aptos.Accounts/Ed25519Account.cs
index ed6964c..2490b76 100644
--- a/Aptos/Aptos.Accounts/Ed25519Account.cs
+++ b/Aptos/Aptos.Accounts/Ed25519Account.cs
@@ -12,12 +12,17 @@ public class Ed25519Account : Account
///
public readonly Ed25519PrivateKey PrivateKey;
- private readonly Ed25519PublicKey _publicKey;
+ private readonly Ed25519PublicKey _verifyingKey;
///
/// Gets the Ed25519PublicKey for the account.
///
- public override AccountPublicKey PublicKey => _publicKey;
+ public override IVerifyingKey VerifyingKey => _verifyingKey;
+
+ ///
+ /// Gets the Ed25519PublicKey for the account.
+ ///
+ public PublicKey PublicKey => _verifyingKey;
private readonly AccountAddress _address;
@@ -50,8 +55,8 @@ public Ed25519Account(Ed25519PrivateKey privateKey, byte[]? address = null)
/// The account address.
public Ed25519Account(Ed25519PrivateKey privateKey, AccountAddress? address = null)
{
- _publicKey = (Ed25519PublicKey)privateKey.PublicKey();
- _address = address ?? PublicKey.AuthKey().DerivedAddress();
+ _verifyingKey = (Ed25519PublicKey)privateKey.PublicKey();
+ _address = address ?? _verifyingKey.AuthKey().DerivedAddress();
PrivateKey = privateKey;
}
@@ -62,15 +67,7 @@ public Ed25519Account(Ed25519PrivateKey privateKey, AccountAddress? address = nu
/// The signed message to verify.
/// True if the signature is valid; otherwise, false.
public bool VerifySignature(byte[] message, Ed25519Signature signature) =>
- PublicKey.VerifySignature(message, signature);
-
- ///
- /// Signs a transaction using the account's private key.
- ///
- /// The transaction to sign.
- /// The transaction signature.
- public override Signature SignTransaction(AnyRawTransaction transaction) =>
- Sign(SigningMessage.GenerateForTransaction(transaction));
+ _verifyingKey.VerifySignature(message, signature);
///
/// Signs a message with the using the account's private key.
@@ -85,17 +82,7 @@ public override Signature SignTransaction(AnyRawTransaction transaction) =>
/// The message to sign as a byte array.
/// The authenticator containing the signature.
public override AccountAuthenticator SignWithAuthenticator(byte[] message) =>
- new AccountAuthenticatorEd25519(_publicKey, (Ed25519Signature)PrivateKey.Sign(message));
-
- ///
- /// Signs a transaction and returns an authenticator with the signature.
- ///
- /// The transaction to sign.
- /// The authenticator containing the signature.
- public override AccountAuthenticator SignTransactionWithAuthenticator(
- AnyRawTransaction transaction
- ) =>
- new AccountAuthenticatorEd25519(_publicKey, (Ed25519Signature)SignTransaction(transaction));
+ new AccountAuthenticatorEd25519(_verifyingKey, (Ed25519Signature)Sign(message));
///
/// Generates a new Ed25519 account.
diff --git a/Aptos/Aptos.Accounts/EphemeralKeyPair.cs b/Aptos/Aptos.Accounts/EphemeralKeyPair.cs
index 06129e2..440e91a 100644
--- a/Aptos/Aptos.Accounts/EphemeralKeyPair.cs
+++ b/Aptos/Aptos.Accounts/EphemeralKeyPair.cs
@@ -46,14 +46,7 @@ public EphemeralKeyPair(
)
{
_privateKey = privateKey;
- if (privateKey.PublicKey() is LegacyAccountPublicKey publicKey)
- {
- PublicKey = new EphemeralPublicKey(publicKey);
- }
- else
- throw new ArgumentException(
- "Invalid PrivateKey passed to EphemeralKeyPair. Expected LegacyAccountPublicKey."
- );
+ PublicKey = new EphemeralPublicKey(privateKey.PublicKey());
// By default, the expiry timestamp is 14 days from now.
ExpiryTimestamp =
@@ -78,13 +71,7 @@ public EphemeralSignature Sign(byte[] data)
if (IsExpired())
throw new Exception("EphemeralKeyPair is expired");
var signature = _privateKey.Sign(data);
- if (signature is LegacySignature legacySignature)
- {
- return new EphemeralSignature(legacySignature);
- }
- throw new ArgumentException(
- "Invalid PrivateKey passed to EphemeralKeyPair. Expecting a legacy private key."
- );
+ return new EphemeralSignature(signature);
}
public override void Serialize(Serializer s)
diff --git a/Aptos/Aptos.Accounts/KeylessAccount.cs b/Aptos/Aptos.Accounts/KeylessAccount.cs
index 485b34a..d0f5ee3 100644
--- a/Aptos/Aptos.Accounts/KeylessAccount.cs
+++ b/Aptos/Aptos.Accounts/KeylessAccount.cs
@@ -10,12 +10,12 @@ public class KeylessAccount : Account
public static readonly string DOMAIN_SEPARATOR = "APTOS::TransactionAndProof";
- private readonly KeylessPublicKey _publicKey;
+ private readonly SingleKey _verifyingKey;
///
- /// Gets the KeylessPublicKey for the account.
+ /// Gets the KeylessPublicKey inside a SingleKey for the account.
///
- public override AccountPublicKey PublicKey => _publicKey;
+ public override IVerifyingKey VerifyingKey => _verifyingKey;
private readonly AccountAddress _address;
@@ -52,8 +52,8 @@ public KeylessAccount(
if (pepper.Length != PEPPER_LENGTH)
throw new ArgumentException($"Pepper length in bytes should be {PEPPER_LENGTH}");
- _publicKey = KeylessPublicKey.FromJwt(jwt, pepper, uidKey);
- _address = address ?? _publicKey.AuthKey().DerivedAddress();
+ _verifyingKey = new SingleKey(KeylessPublicKey.FromJwt(jwt, pepper, uidKey));
+ _address = address ?? _verifyingKey.AuthKey().DerivedAddress();
EphemeralKeyPair = ekp;
Proof = proof;
Pepper = pepper;
@@ -73,7 +73,7 @@ public bool VerifySignature(byte[] message, KeylessSignature signature)
return EphemeralKeyPair.PublicKey.VerifySignature(message, signature.EphemeralSignature);
}
- public override Signature SignTransaction(AnyRawTransaction transaction)
+ public override Signature Sign(AnyRawTransaction transaction)
{
RawTransaction rawTxn = transaction.RawTransaction;
Serializer s = new();
@@ -101,16 +101,15 @@ public override Signature Sign(byte[] message)
public override AccountAuthenticator SignWithAuthenticator(byte[] message) =>
new AccountAuthenticatorSingleKey(
- new AnyPublicKey(_publicKey),
- new AnySignature(Sign(message))
+ _verifyingKey.PublicKey,
+ (PublicKeySignature)Sign(message)
);
- public override AccountAuthenticator SignTransactionWithAuthenticator(
- AnyRawTransaction transaction
- ) =>
+ public override AccountAuthenticator SignWithAuthenticator(AnyRawTransaction transaction) =>
new AccountAuthenticatorSingleKey(
- new AnyPublicKey(_publicKey),
- new AnySignature(SignTransaction(transaction))
+ _verifyingKey.PublicKey,
+ // Have to declare explictly because the virtual method signs without proof
+ (PublicKeySignature)Sign(transaction)
);
public void Serialize(Serializer s)
diff --git a/Aptos/Aptos.Accounts/MultiKeyAccount.cs b/Aptos/Aptos.Accounts/MultiKeyAccount.cs
index 6256af0..61ff908 100644
--- a/Aptos/Aptos.Accounts/MultiKeyAccount.cs
+++ b/Aptos/Aptos.Accounts/MultiKeyAccount.cs
@@ -10,17 +10,17 @@ namespace Aptos;
///
public class MultiKeyAccount : Account
{
- private readonly MultiKey _publicKey;
+ private readonly MultiKey _verifyingKey;
///
/// Gets the MultiKeyPublicKey for the account.
///
- public override AccountPublicKey PublicKey => _publicKey;
+ public override IVerifyingKey VerifyingKey => _verifyingKey;
///
/// Gets the address of the account.
///
- public override AccountAddress Address => PublicKey.AuthKey().DerivedAddress();
+ public override AccountAddress Address => _verifyingKey.AuthKey().DerivedAddress();
///
/// The signers used to sign messages. These signers should correspond to public keys in the
@@ -52,20 +52,29 @@ public class MultiKeyAccount : Account
/// If the signers do not correspond to public keys in the MultiKeyAccount's public key.
public MultiKeyAccount(MultiKey multiKey, List signers)
{
- _publicKey = multiKey;
+ _verifyingKey = multiKey;
// Get the index of each respective signer in the bitmpa
List bitPositions = [];
for (int i = 0; i < signers.Count; i++)
{
var signer = signers[i];
- bitPositions.Add(multiKey.GetIndex(signer.PublicKey));
+ if (signer.VerifyingKey is SingleKey singleKey)
+ {
+ bitPositions.Add(multiKey.GetIndex(singleKey.PublicKey));
+ }
+ else if (signer.VerifyingKey is Ed25519PublicKey ed25519PublicKey)
+ {
+ bitPositions.Add(multiKey.GetIndex(ed25519PublicKey));
+ }
+ else
+ {
+ throw new ArgumentException(
+ "MultiKeyAccount cannot be used with unified verifying keys (e.g. MultiKeyPublicKey)"
+ );
+ }
}
- if (signers.Any(s => s.PublicKey is UnifiedAccountPublicKey))
- throw new ArgumentException(
- "MultiKeyAccount cannot be used with unified account public keys (e.g. AnyPublicKey or MultiKeyPublicKey)"
- );
if (multiKey.SignaturesRequired > signers.Count)
throw new ArgumentException(
$"Signatures required must be less than or equal to the number of signers"
@@ -101,7 +110,7 @@ public bool VerifySignature(byte[] message, MultiKeySignature signature)
for (int i = 0; i < signature.Signatures.Count; i++)
{
Signature singleSignature = signature.Signatures[i];
- PublicKey singlePublicKey = _publicKey.PublicKeys[SignerIndicies[i]];
+ PublicKey singlePublicKey = _verifyingKey.PublicKeys[SignerIndicies[i]];
if (!singlePublicKey.VerifySignature(message, singleSignature))
return false;
}
@@ -116,18 +125,15 @@ public bool VerifySignature(byte[] message, MultiKeySignature signature)
/// The signed message.
public override Signature Sign(byte[] message) =>
new MultiKeySignature(
- Signers.Select(s => s.Sign(message)).ToList(),
- MultiKey.CreateBitmap(SignerIndicies)
- );
-
- ///
- /// Signs a transaction using the signer.
- ///
- /// The transaction to sign.
- /// The transaction signature.
- public override Signature SignTransaction(AnyRawTransaction transaction) =>
- new MultiKeySignature(
- Signers.Select(s => s.SignTransaction(transaction)).ToList(),
+ Signers
+ .Select(s =>
+ s.Sign(message) is PublicKeySignature publicKeySignature
+ ? publicKeySignature
+ : throw new Exception(
+ "MultiKeyAccount cannot be used with unified accounts (e.g. MultiKeyAccount)"
+ )
+ )
+ .ToList(),
MultiKey.CreateBitmap(SignerIndicies)
);
@@ -137,18 +143,5 @@ public override Signature SignTransaction(AnyRawTransaction transaction) =>
/// The message to sign as a byte array.
/// The authenticator containing the signature.
public override AccountAuthenticator SignWithAuthenticator(byte[] message) =>
- new AccountAuthenticatorMultiKey(_publicKey, (MultiKeySignature)Sign(message));
-
- ///
- /// Signs a message and returns an authenticator with the signature.
- ///
- /// The message to sign as a byte array.
- /// The authenticator containing the signature.
- public override AccountAuthenticator SignTransactionWithAuthenticator(
- AnyRawTransaction transaction
- ) =>
- new AccountAuthenticatorMultiKey(
- _publicKey,
- (MultiKeySignature)SignTransaction(transaction)
- );
+ new AccountAuthenticatorMultiKey(_verifyingKey, (MultiKeySignature)Sign(message));
}
diff --git a/Aptos/Aptos.Accounts/SingleKeyAccount.cs b/Aptos/Aptos.Accounts/SingleKeyAccount.cs
index dae6743..bb0146c 100644
--- a/Aptos/Aptos.Accounts/SingleKeyAccount.cs
+++ b/Aptos/Aptos.Accounts/SingleKeyAccount.cs
@@ -1,13 +1,15 @@
-namespace Aptos;
-
using Aptos.Schemes;
+namespace Aptos;
+
public class SingleKeyAccount : Account
{
public readonly PrivateKey PrivateKey;
- private readonly AnyPublicKey _publicKey;
- public override AccountPublicKey PublicKey => _publicKey;
+ private readonly SingleKey _verifyingKey;
+ public override IVerifyingKey VerifyingKey => _verifyingKey;
+
+ public PublicKey PublicKey => _verifyingKey.PublicKey;
private readonly AccountAddress _address;
public override AccountAddress Address => _address;
@@ -25,32 +27,26 @@ public SingleKeyAccount(PrivateKey privateKey, string? address = null)
public SingleKeyAccount(PrivateKey privateKey, AccountAddress? address = null)
{
- _publicKey = new AnyPublicKey(privateKey.PublicKey());
- _address = address ?? _publicKey.AuthKey().DerivedAddress();
+ _verifyingKey = new SingleKey(privateKey.PublicKey());
+ _address = address ?? _verifyingKey.AuthKey().DerivedAddress();
PrivateKey = privateKey;
}
- public bool VerifySignature(string message, AnySignature signature) =>
- PublicKey.VerifySignature(message, signature);
-
- public bool VerifySignature(byte[] message, AnySignature signature) =>
- PublicKey.VerifySignature(message, signature);
+ public bool VerifySignature(string message, Signature signature) =>
+ _verifyingKey.VerifySignature(message, signature);
- public override Signature SignTransaction(AnyRawTransaction transaction) =>
- Sign(SigningMessage.GenerateForTransaction(transaction));
+ public bool VerifySignature(byte[] message, Signature signature) =>
+ _verifyingKey.VerifySignature(message, signature);
- public override Signature Sign(byte[] message) => new AnySignature(PrivateKey.Sign(message));
+ public override Signature Sign(byte[] message) => PrivateKey.Sign(message);
public override AccountAuthenticator SignWithAuthenticator(byte[] message) =>
- new AccountAuthenticatorSingleKey(_publicKey, (AnySignature)Sign(message));
-
- public override AccountAuthenticator SignTransactionWithAuthenticator(
- AnyRawTransaction transaction
- ) => new AccountAuthenticatorSingleKey(_publicKey, (AnySignature)SignTransaction(transaction));
-
- public static new SingleKeyAccount Generate() => Generate(PublicKeyVariant.Ed25519);
+ new AccountAuthenticatorSingleKey(
+ _verifyingKey.PublicKey,
+ (PublicKeySignature)Sign(message)
+ );
- public static SingleKeyAccount Generate(PublicKeyVariant scheme)
+ public static SingleKeyAccount Generate(PublicKeyVariant scheme = PublicKeyVariant.Ed25519)
{
PrivateKey privateKey = scheme switch
{
diff --git a/Aptos/Aptos.Api/AccountSignature.cs b/Aptos/Aptos.Api/AccountSignature.cs
index 9b4f2ca..23ab12f 100644
--- a/Aptos/Aptos.Api/AccountSignature.cs
+++ b/Aptos/Aptos.Api/AccountSignature.cs
@@ -65,38 +65,36 @@ public class AccountEd25519Signature(Hex publicKey, Hex signature)
public Hex Signature = signature;
}
-public class AccountSingleKeySignature(ILegacyPublicKey publicKey, LegacySignature signature)
+public class AccountSingleKeySignature(PublicKey publicKey, PublicKeySignature signature)
: AccountSignature(SigningScheme.SingleKey)
{
[JsonProperty("public_key")]
- public ILegacyPublicKey PublicKey = publicKey;
+ public PublicKey PublicKey = publicKey;
[JsonProperty("signature")]
- public LegacySignature Signature = signature;
+ public PublicKeySignature Signature = signature;
}
public class AccountMultiKeySignature(
- List publicKeys,
- List signatures,
+ List publicKeys,
+ List signatures,
byte signaturesRequired
) : AccountSignature(SigningScheme.MultiKey)
{
- public class IndexedAccountSignature(byte index, LegacySignature signature)
+ public class IndexedAccountSignature(byte index, PublicKeySignature signature)
{
[JsonProperty("index")]
public byte Index = index;
[JsonProperty("signature")]
- public LegacySignature Signature = signature;
+ public PublicKeySignature Signature = signature;
}
[JsonProperty("public_keys")]
- public List PublicKeys = publicKeys;
+ public List PublicKeys = publicKeys;
[JsonProperty("signatures")]
- public List Signatures = signatures
- .Select((sig, i) => new IndexedAccountSignature((byte)i, sig))
- .ToList();
+ public List Signatures = signatures;
[JsonProperty("signatures_required")]
public byte SignaturesRequired = signaturesRequired;
diff --git a/Aptos/Aptos.Api/TransactionSignature.cs b/Aptos/Aptos.Api/TransactionSignature.cs
index a9edcdc..dd5e5a0 100644
--- a/Aptos/Aptos.Api/TransactionSignature.cs
+++ b/Aptos/Aptos.Api/TransactionSignature.cs
@@ -92,6 +92,21 @@ JsonSerializer serializer
);
}
}
+ else if (jsonObject.ContainsKey("public_keys"))
+ {
+ var publicKeys = jsonObject["public_keys"]?.ToString();
+ var signatures = jsonObject["signatures"]?.ToString();
+ var signaturesRequired = jsonObject["signatures_required"]?.ToString();
+
+ // AccountSignature_MultiKeySignature
+ if (publicKeys != null && signatures != null && signaturesRequired != null)
+ {
+ jsonObject["type"] = "multi_key_signature";
+ signature = JsonConvert.DeserializeObject(
+ jsonObject.ToString()
+ );
+ }
+ }
if (signature == null)
throw new Exception("Invalid account signature");
diff --git a/Aptos/Aptos.Clients/KeylessClient.cs b/Aptos/Aptos.Clients/KeylessClient.cs
index c562a3e..32f57bf 100644
--- a/Aptos/Aptos.Clients/KeylessClient.cs
+++ b/Aptos/Aptos.Clients/KeylessClient.cs
@@ -20,8 +20,9 @@ public async Task DeriveAccount(
// Derive the keyless account from the JWT and EphemeralKeyPair
var publicKey = KeylessPublicKey.FromJwt(jwt, pepper, uidKey);
+
var address = await _client.Account.LookupOriginalAccountAddress(
- publicKey.AuthKey().DerivedAddress().ToString()
+ new SingleKey(publicKey).AuthKey().DerivedAddress().ToString()
);
// Create and return the keyless account
diff --git a/Aptos/Aptos.Clients/TransactionClient.cs b/Aptos/Aptos.Clients/TransactionClient.cs
index 7509dbc..f514e49 100644
--- a/Aptos/Aptos.Clients/TransactionClient.cs
+++ b/Aptos/Aptos.Clients/TransactionClient.cs
@@ -16,7 +16,7 @@ public class TransactionClient(AptosClient client)
/// The transaction to sign.
/// The authenticator with the signed transaction and public key.
public AccountAuthenticator SignTransaction(Account signer, AnyRawTransaction transaction) =>
- signer.SignTransactionWithAuthenticator(transaction);
+ signer.SignWithAuthenticator(transaction);
///
/// Submits a transaction to the blockchain.
diff --git a/Aptos/Aptos.Crypto/AccountPublicKey.cs b/Aptos/Aptos.Crypto/AccountPublicKey.cs
deleted file mode 100644
index eea876a..0000000
--- a/Aptos/Aptos.Crypto/AccountPublicKey.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-namespace Aptos;
-
-using System.Runtime.Serialization;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
-using Newtonsoft.Json.Linq;
-
-public abstract class AccountPublicKey : PublicKey
-{
- public abstract AuthenticationKey AuthKey();
-}
-
-[JsonConverter(typeof(StringEnumConverter))]
-public enum PublicKeyVariant : uint
-{
- [EnumMember(Value = "ed25519")]
- Ed25519,
-
- [EnumMember(Value = "secp256k1_ecdsa")]
- Secp256k1Ecdsa,
-
- [EnumMember(Value = "secp256r1_ecdsa")]
- Secp256r1Ecdsa,
-
- [EnumMember(Value = "keyless")]
- Keyless,
-}
-
-[JsonConverter(typeof(LegacyPublicKeyConverter))]
-public interface ILegacyPublicKey
-{
- [JsonProperty("type")]
- public PublicKeyVariant Type { get; }
-
- [JsonProperty("value")]
- public Hex Value { get; }
-}
-
-public abstract class LegacyPublicKey(PublicKeyVariant type) : PublicKey, ILegacyPublicKey
-{
- private readonly PublicKeyVariant _type = type;
- public PublicKeyVariant Type => _type;
-
- public abstract Hex Value { get; }
-}
-
-public class LegacyPublicKeyConverter : JsonConverter
-{
- public override ILegacyPublicKey? ReadJson(
- JsonReader reader,
- Type objectType,
- ILegacyPublicKey? existingValue,
- bool hasExistingValue,
- JsonSerializer serializer
- )
- {
- var jsonObject = JObject.Load(reader);
- var type = jsonObject["type"]?.ToString();
-
- AnyValue? anyValue = JsonConvert.DeserializeObject(jsonObject.ToString());
- if (anyValue == null)
- throw new Exception("Invalid public key shape");
-
- Deserializer deserializer = new(anyValue.Value);
- deserializer.Uleb128AsU32();
-
- return type switch
- {
- "ed25519" => new Ed25519PublicKey(anyValue.Value),
- "secp256k1_ecdsa" => new Secp256k1PublicKey(anyValue.Value),
- "keyless" => KeylessPublicKey.Deserialize(new Deserializer(anyValue.Value)),
- _ => throw new Exception($"Unknown public key type: {type}"),
- };
- }
-
- public override void WriteJson(
- JsonWriter writer,
- ILegacyPublicKey? value,
- JsonSerializer serializer
- )
- {
- if (value == null)
- writer.WriteNull();
- else
- {
- writer.WriteStartObject();
- writer.WritePropertyName("type");
- writer.WriteValue(JsonConvert.SerializeObject(value.Type).Replace("\"", ""));
- writer.WritePropertyName("value");
- writer.WriteValue(value.Value.ToString());
- writer.WriteEndObject();
- }
- }
-}
-
-public abstract class UnifiedAccountPublicKey : AccountPublicKey { }
-
-public abstract class LegacyAccountPublicKey(PublicKeyVariant type)
- : AccountPublicKey,
- ILegacyPublicKey
-{
- private readonly PublicKeyVariant _type = type;
- public PublicKeyVariant Type => _type;
-
- public abstract Hex Value { get; }
-}
diff --git a/Aptos/Aptos.Crypto/Ed25519.cs b/Aptos/Aptos.Crypto/Ed25519.cs
index 5a5286c..14ff7a9 100644
--- a/Aptos/Aptos.Crypto/Ed25519.cs
+++ b/Aptos/Aptos.Crypto/Ed25519.cs
@@ -62,7 +62,7 @@ public static bool IsCanonicalEd25519Signature(Signature signature)
}
}
-public class Ed25519PublicKey : LegacyAccountPublicKey
+public class Ed25519PublicKey : PublicKey, IVerifyingKey
{
static readonly int LENGTH = 32;
@@ -83,7 +83,7 @@ public Ed25519PublicKey(byte[] publicKey)
public override byte[] ToByteArray() => _key.ToByteArray();
- public override AuthenticationKey AuthKey() =>
+ public AuthenticationKey AuthKey() =>
AuthenticationKey.FromSchemeAndBytes(AuthenticationKeyScheme.Ed25519, _key.ToByteArray());
public override bool VerifySignature(byte[] message, Signature signature)
@@ -102,7 +102,7 @@ public override bool VerifySignature(byte[] message, Signature signature)
public override void Serialize(Serializer s) => s.Bytes(_key.ToByteArray());
- public static Ed25519PublicKey Deserialize(Deserializer d) => new(d.Bytes());
+ public static new Ed25519PublicKey Deserialize(Deserializer d) => new(d.Bytes());
}
public class Ed25519PrivateKey : PrivateKey
@@ -129,9 +129,7 @@ public override PublicKey PublicKey() =>
new Ed25519PrivateKeyParameters(_key.ToByteArray(), 0).GeneratePublicKey().GetEncoded()
);
- public override Signature Sign(string message) => Sign(SigningMessage.Convert(message));
-
- public override Signature Sign(byte[] message)
+ public override PublicKeySignature Sign(byte[] message)
{
Ed25519Signer signer = new();
signer.Init(true, new Ed25519PrivateKeyParameters(_key.ToByteArray(), 0));
@@ -174,7 +172,7 @@ public static Ed25519PrivateKey FromDerivationPath(string path, string mnemonic)
public static Ed25519PrivateKey Deserialize(Deserializer d) => new(d.Bytes());
}
-public class Ed25519Signature : LegacySignature
+public class Ed25519Signature : PublicKeySignature
{
static readonly int LENGTH = 64;
@@ -186,7 +184,7 @@ public Ed25519Signature(string signature)
: this(Hex.FromHexInput(signature).ToByteArray()) { }
public Ed25519Signature(byte[] signature)
- : base(SignatureVariant.Ed25519)
+ : base(PublicKeySignatureVariant.Ed25519)
{
if (signature.Length != LENGTH)
throw new KeyLengthMismatch("Ed25519Signature", LENGTH);
@@ -197,5 +195,5 @@ public Ed25519Signature(byte[] signature)
public override void Serialize(Serializer s) => s.Bytes(_value.ToByteArray());
- public static Ed25519Signature Deserialize(Deserializer d) => new(d.Bytes());
+ public static new Ed25519Signature Deserialize(Deserializer d) => new(d.Bytes());
}
diff --git a/Aptos/Aptos.Crypto/Ephemeral.cs b/Aptos/Aptos.Crypto/Ephemeral.cs
index 4070b36..5226b2f 100644
--- a/Aptos/Aptos.Crypto/Ephemeral.cs
+++ b/Aptos/Aptos.Crypto/Ephemeral.cs
@@ -2,13 +2,13 @@ namespace Aptos;
using Aptos.Exceptions;
-public class EphemeralPublicKey : LegacyPublicKey
+public class EphemeralPublicKey : PublicKey
{
- public readonly LegacyAccountPublicKey PublicKey;
+ public readonly PublicKey PublicKey;
public override Hex Value => PublicKey.BcsToHex();
- public EphemeralPublicKey(LegacyAccountPublicKey publicKey)
+ public EphemeralPublicKey(PublicKey publicKey)
: base(publicKey.Type)
{
PublicKey = publicKey;
@@ -34,7 +34,7 @@ public override void Serialize(Serializer s)
PublicKey.Serialize(s);
}
- public static EphemeralPublicKey Deserialize(Deserializer d)
+ public static new EphemeralPublicKey Deserialize(Deserializer d)
{
PublicKeyVariant variant = (PublicKeyVariant)d.Uleb128AsU32();
return variant switch
@@ -45,18 +45,19 @@ public static EphemeralPublicKey Deserialize(Deserializer d)
}
}
-public class EphemeralSignature : Signature
+public class EphemeralSignature : PublicKeySignature
{
- public readonly Signature Signature;
+ public readonly PublicKeySignature Signature;
- public readonly SignatureVariant Type;
+ public override Hex Value => Signature.Value;
- public EphemeralSignature(LegacySignature signature)
+ public EphemeralSignature(PublicKeySignature signature)
+ : base(signature.Type)
{
Signature = signature;
switch (signature.Type)
{
- case SignatureVariant.Ed25519:
+ case PublicKeySignatureVariant.Ed25519:
break;
default:
throw new EphemeralSignatureVariantUnsupported(signature.Type);
@@ -71,13 +72,107 @@ public override void Serialize(Serializer s)
Signature.Serialize(s);
}
- public static EphemeralSignature Deserialize(Deserializer d)
+ public static new EphemeralSignature Deserialize(Deserializer d)
{
- SignatureVariant variant = (SignatureVariant)d.Uleb128AsU32();
+ PublicKeySignatureVariant variant = (PublicKeySignatureVariant)d.Uleb128AsU32();
return variant switch
{
- SignatureVariant.Ed25519 => new EphemeralSignature(Ed25519Signature.Deserialize(d)),
+ PublicKeySignatureVariant.Ed25519 => new EphemeralSignature(
+ Ed25519Signature.Deserialize(d)
+ ),
_ => throw new EphemeralSignatureVariantUnsupported(variant),
};
}
}
+
+public enum EphemeralSignatureVariant : uint
+{
+ ZkProof = 0,
+}
+
+public abstract class CertificateSignature : Serializable
+{
+ public abstract byte[] ToByteArray();
+
+ public override string ToString() => Hex.FromHexInput(ToByteArray()).ToString();
+}
+
+public class EphemeralCertificate : Serializable
+{
+ public readonly CertificateSignature Signature;
+
+ public readonly EphemeralSignatureVariant Variant;
+
+ public EphemeralCertificate(CertificateSignature signature, EphemeralSignatureVariant variant)
+ {
+ Signature = signature;
+ Variant = variant;
+ }
+
+ public byte[] ToByteArray() => Signature.ToByteArray();
+
+ public override void Serialize(Serializer s)
+ {
+ s.U32AsUleb128((uint)Variant);
+ Signature.Serialize(s);
+ }
+
+ public static EphemeralCertificate Deserialize(Deserializer d)
+ {
+ EphemeralSignatureVariant variant = (EphemeralSignatureVariant)d.Uleb128AsU32();
+ return variant switch
+ {
+ EphemeralSignatureVariant.ZkProof => new EphemeralCertificate(
+ ZeroKnowledgeSignature.Deserialize(d),
+ EphemeralSignatureVariant.ZkProof
+ ),
+ _ => throw new ArgumentException("Invalid signature variant"),
+ };
+ }
+}
+
+public class ZeroKnowledgeSignature(
+ ZkProof proof,
+ ulong expHorizonSecs,
+ string? extraField = null,
+ string? overrideAudVal = null,
+ EphemeralSignature? trainingWheelSignature = null
+) : CertificateSignature
+{
+ public readonly ZkProof Proof = proof;
+
+ public readonly ulong ExpHorizonSecs = expHorizonSecs;
+
+ public readonly string? ExtraField = extraField;
+
+ public readonly string? OverrideAudVal = overrideAudVal;
+
+ public readonly EphemeralSignature? TrainingWheelSignature = trainingWheelSignature;
+
+ public override byte[] ToByteArray() => BcsToBytes();
+
+ public override void Serialize(Serializer s)
+ {
+ Proof.Serialize(s);
+ s.U64(ExpHorizonSecs);
+ s.OptionString(ExtraField);
+ s.OptionString(OverrideAudVal);
+ s.Option(TrainingWheelSignature);
+ }
+
+ public static ZeroKnowledgeSignature Deserialize(Deserializer d)
+ {
+ ZkProof proof = ZkProof.Deserialize(d);
+ ulong expHorizonSecs = d.U64();
+ string? extraField = d.OptionString();
+ string? overrideAudVal = d.OptionString();
+ EphemeralSignature? trainingWheelSignature = d.Option(EphemeralSignature.Deserialize);
+ return new ZeroKnowledgeSignature(
+ proof,
+ expHorizonSecs,
+ extraField,
+ overrideAudVal,
+ trainingWheelSignature
+ );
+ }
+}
diff --git a/Aptos/Aptos.Crypto/IVerifyingKey.cs b/Aptos/Aptos.Crypto/IVerifyingKey.cs
new file mode 100644
index 0000000..42e9ce8
--- /dev/null
+++ b/Aptos/Aptos.Crypto/IVerifyingKey.cs
@@ -0,0 +1,15 @@
+namespace Aptos;
+
+///
+/// A verifying key represents a collection of public keys that can be used to verify signatures
+/// or derive authentication keys. This interface is typically implemented to collect public keys
+/// for Account Authenticators.
+///
+public interface IVerifyingKey
+{
+ public AuthenticationKey AuthKey();
+
+ public bool VerifySignature(string message, Signature signature);
+
+ public bool VerifySignature(byte[] message, Signature signature);
+}
diff --git a/Aptos/Aptos.Crypto/Keyless.cs b/Aptos/Aptos.Crypto/Keyless.cs
index e53c4d2..c800ac8 100644
--- a/Aptos/Aptos.Crypto/Keyless.cs
+++ b/Aptos/Aptos.Crypto/Keyless.cs
@@ -2,7 +2,6 @@ namespace Aptos;
using System.Numerics;
using Aptos.Poseidon;
-using Aptos.Schemes;
using Microsoft.IdentityModel.JsonWebTokens;
public static class Keyless
@@ -54,9 +53,27 @@ byte[] pepper
KeylessPublicKey.ID_COMMITMENT_LENGTH
);
}
+
+ public static KeylessSignature GetSimulationSignature() =>
+ new(
+ ephemeralCertificate: new EphemeralCertificate(
+ new ZeroKnowledgeSignature(
+ proof: new ZkProof(
+ new Groth16Zkp(new byte[32], new byte[64], new byte[32]),
+ ZkpVariant.Groth16
+ ),
+ expHorizonSecs: 0
+ ),
+ variant: EphemeralSignatureVariant.ZkProof
+ ),
+ jwtHeader: "{}",
+ expiryDateSecs: 0,
+ ephemeralPublicKey: new EphemeralPublicKey(new Ed25519PublicKey(new byte[32])),
+ ephemeralSignature: new EphemeralSignature(new Ed25519Signature(new byte[64]))
+ );
}
-public class KeylessPublicKey : LegacyAccountPublicKey
+public class KeylessPublicKey : PublicKey
{
public static readonly int ID_COMMITMENT_LENGTH = 32;
@@ -84,16 +101,12 @@ public KeylessPublicKey(string iss, byte[] idCommitment)
IdCommitment = idCommitment;
}
- public override AuthenticationKey AuthKey()
- {
- Serializer s = new();
- s.U32AsUleb128((uint)Type);
- s.FixedBytes(BcsToBytes());
- return AuthenticationKey.FromSchemeAndBytes(AuthenticationKeyScheme.SingleKey, s.ToBytes());
- }
-
public override bool VerifySignature(byte[] message, Signature signature) =>
- throw new NotImplementedException();
+ signature is KeylessSignature keylessSignature
+ && keylessSignature.EphemeralPublicKey.VerifySignature(
+ message,
+ keylessSignature.EphemeralSignature.Signature
+ );
public override byte[] ToByteArray() => BcsToBytes();
@@ -103,7 +116,7 @@ public override void Serialize(Serializer s)
s.Bytes(IdCommitment);
}
- public static KeylessPublicKey Deserialize(Deserializer d)
+ public static new KeylessPublicKey Deserialize(Deserializer d)
{
string iss = d.String();
byte[] idCommitment = d.Bytes();
@@ -117,7 +130,7 @@ public static KeylessPublicKey FromJwt(string jwt, byte[] pepper, string uidKey
new(new JsonWebToken(jwt).Issuer, Keyless.ComputeIdCommitment(jwt, pepper, uidKey));
}
-public class KeylessSignature : LegacySignature
+public class KeylessSignature : PublicKeySignature
{
public readonly EphemeralCertificate EphemeralCertificate;
@@ -138,7 +151,7 @@ public KeylessSignature(
EphemeralPublicKey ephemeralPublicKey,
EphemeralSignature ephemeralSignature
)
- : base(SignatureVariant.Keyless)
+ : base(PublicKeySignatureVariant.Keyless)
{
EphemeralCertificate = ephemeralCertificate;
JwtHeader = jwtHeader;
@@ -158,7 +171,7 @@ public override void Serialize(Serializer s)
EphemeralSignature.Serialize(s);
}
- public static KeylessSignature Deserialize(Deserializer d)
+ public static new KeylessSignature Deserialize(Deserializer d)
{
EphemeralCertificate ephemeralCertificate = EphemeralCertificate.Deserialize(d);
string jwtHeader = d.String();
@@ -174,88 +187,3 @@ public static KeylessSignature Deserialize(Deserializer d)
);
}
}
-
-public enum EphemeralSignatureVariant : uint
-{
- ZkProof = 0,
-}
-
-public class EphemeralCertificate : Signature
-{
- public readonly Signature Signature;
-
- public readonly EphemeralSignatureVariant Variant;
-
- public EphemeralCertificate(Signature signature, EphemeralSignatureVariant variant)
- {
- Signature = signature;
- Variant = variant;
- }
-
- public override byte[] ToByteArray() => Signature.ToByteArray();
-
- public override void Serialize(Serializer s)
- {
- s.U32AsUleb128((uint)Variant);
- Signature.Serialize(s);
- }
-
- public static EphemeralCertificate Deserialize(Deserializer d)
- {
- EphemeralSignatureVariant variant = (EphemeralSignatureVariant)d.Uleb128AsU32();
- return variant switch
- {
- EphemeralSignatureVariant.ZkProof => new EphemeralCertificate(
- ZeroKnowledgeSignature.Deserialize(d),
- EphemeralSignatureVariant.ZkProof
- ),
- _ => throw new ArgumentException("Invalid signature variant"),
- };
- }
-}
-
-public class ZeroKnowledgeSignature(
- ZkProof proof,
- ulong expHorizonSecs,
- string? extraField,
- string? overrideAudVal,
- EphemeralSignature? trainingWheelSignature
-) : Signature
-{
- public readonly ZkProof Proof = proof;
-
- public readonly ulong ExpHorizonSecs = expHorizonSecs;
-
- public readonly string? ExtraField = extraField;
-
- public readonly string? OverrideAudVal = overrideAudVal;
-
- public readonly EphemeralSignature? TrainingWheelSignature = trainingWheelSignature;
-
- public override byte[] ToByteArray() => BcsToBytes();
-
- public override void Serialize(Serializer s)
- {
- Proof.Serialize(s);
- s.U64(ExpHorizonSecs);
- s.OptionString(ExtraField);
- s.OptionString(OverrideAudVal);
- s.Option(TrainingWheelSignature);
- }
-
- public static ZeroKnowledgeSignature Deserialize(Deserializer d)
- {
- ZkProof proof = ZkProof.Deserialize(d);
- ulong expHorizonSecs = d.U64();
- string? extraField = d.OptionString();
- string? overrideAudVal = d.OptionString();
- EphemeralSignature? trainingWheelSignature = d.Option(EphemeralSignature.Deserialize);
- return new ZeroKnowledgeSignature(
- proof,
- expHorizonSecs,
- extraField,
- overrideAudVal,
- trainingWheelSignature
- );
- }
-}
diff --git a/Aptos/Aptos.Crypto/MultiKey.cs b/Aptos/Aptos.Crypto/MultiKey.cs
index 3064bf8..cefa41b 100644
--- a/Aptos/Aptos.Crypto/MultiKey.cs
+++ b/Aptos/Aptos.Crypto/MultiKey.cs
@@ -42,9 +42,9 @@ public static int BitCount(byte b)
}
}
-public partial class MultiKey : UnifiedAccountPublicKey
+public partial class MultiKey : Serializable, IVerifyingKey
{
- public readonly List PublicKeys;
+ public readonly List PublicKeys;
public readonly byte SignaturesRequired;
@@ -58,68 +58,67 @@ public MultiKey(List publicKeys, byte signaturesRequired)
);
// Make sure that all public keys are normalized to the SingleKey authentication scheme
- PublicKeys = publicKeys
- .Select(p => p is AnyPublicKey anyPublicKey ? anyPublicKey : new AnyPublicKey(p))
- .ToList();
+ PublicKeys = publicKeys;
SignaturesRequired = signaturesRequired;
}
+ public AuthenticationKey AuthKey() =>
+ AuthenticationKey.FromSchemeAndBytes(AuthenticationKeyScheme.MultiKey, BcsToBytes());
+
public int GetIndex(PublicKey publicKey)
{
- AnyPublicKey pubkey = publicKey is AnyPublicKey anyPublicKey
- ? anyPublicKey
- : new AnyPublicKey(publicKey);
- int index = PublicKeys.FindIndex((pk) => pk.ToString().Equals(pubkey.ToString()));
+ int index = PublicKeys.FindIndex((pk) => pk.ToString().Equals(publicKey.ToString()));
if (index == -1)
throw new ArgumentException("Public key not found");
return index;
}
- public override byte[] ToByteArray() => BcsToBytes();
+ public bool VerifySignature(string message, Signature signature) =>
+ VerifySignature(SigningMessage.Convert(message), signature);
- public override AuthenticationKey AuthKey() =>
- AuthenticationKey.FromSchemeAndBytes(AuthenticationKeyScheme.MultiKey, BcsToBytes());
-
- public override bool VerifySignature(byte[] message, Signature signature) =>
+ public bool VerifySignature(byte[] message, Signature signature) =>
throw new NotImplementedException();
public override void Serialize(Serializer s)
{
- s.Vector(PublicKeys);
+ s.U32AsUleb128((uint)PublicKeys.Count);
+ PublicKeys.ForEach(e =>
+ {
+ s.U32AsUleb128((uint)e.Type);
+ e.Serialize(s);
+ });
s.U8(SignaturesRequired);
}
public static MultiKey Deserialize(Deserializer d)
{
- List publicKeys = d.Vector(AnyPublicKey.Deserialize).Cast().ToList();
+ List publicKeys = [.. d.Vector(PublicKey.Deserialize)];
byte signaturesRequired = d.U8();
return new MultiKey(publicKeys, signaturesRequired);
}
}
-public class MultiKeySignature : UnifiedSignature
+public class MultiKeySignature : Signature
{
public static readonly int BITMAP_LEN = 4;
public static readonly int MAX_SIGNATURES_SUPPORTED = BITMAP_LEN * 8;
- public readonly List Signatures;
+ public readonly List Signatures;
public readonly byte[] Bitmap;
- public MultiKeySignature(List signatures, int[] bitmap)
+ public MultiKeySignature(List signatures, int[] bitmap)
: this(signatures, MultiKey.CreateBitmap(bitmap)) { }
- public MultiKeySignature(List signatures, byte[] bitmap)
+ public MultiKeySignature(List signatures, byte[] bitmap)
{
// Make sure that all signatures are normalized to the SingleKey authentication scheme
if (signatures.Count > MAX_SIGNATURES_SUPPORTED)
throw new ArgumentException(
$"Signatures count should be less than or equal to {MAX_SIGNATURES_SUPPORTED}"
);
- Signatures = signatures
- .Select(s => s is AnySignature anySignature ? anySignature : new AnySignature(s))
- .ToList();
+ Signatures = signatures;
// Make sure that the bitmap is the correct length
if (bitmap.Length != BITMAP_LEN)
@@ -137,13 +136,18 @@ public MultiKeySignature(List signatures, byte[] bitmap)
public override void Serialize(Serializer s)
{
- s.Vector(Signatures);
+ s.U32AsUleb128((uint)Signatures.Count);
+ Signatures.ForEach(e =>
+ {
+ s.U32AsUleb128((uint)e.Type);
+ e.Serialize(s);
+ });
s.Bytes(Bitmap);
}
public static MultiKeySignature Deserialize(Deserializer d)
{
- List signatures = d.Vector(AnySignature.Deserialize).Cast().ToList();
+ List signatures = [.. d.Vector(PublicKeySignature.Deserialize)];
byte[] bitmap = d.Bytes();
return new MultiKeySignature(signatures, bitmap);
}
diff --git a/Aptos/Aptos.Crypto/PrivateKey.cs b/Aptos/Aptos.Crypto/PrivateKey.cs
index 15eec92..425dd78 100644
--- a/Aptos/Aptos.Crypto/PrivateKey.cs
+++ b/Aptos/Aptos.Crypto/PrivateKey.cs
@@ -2,9 +2,9 @@ namespace Aptos;
public abstract class PrivateKey : Serializable
{
- public abstract Signature Sign(byte[] message);
+ public virtual PublicKeySignature Sign(string message) => Sign(SigningMessage.Convert(message));
- public abstract Signature Sign(string message);
+ public abstract PublicKeySignature Sign(byte[] message);
public abstract PublicKey PublicKey();
diff --git a/Aptos/Aptos.Crypto/PublicKey.cs b/Aptos/Aptos.Crypto/PublicKey.cs
index 94e5482..67b7d69 100644
--- a/Aptos/Aptos.Crypto/PublicKey.cs
+++ b/Aptos/Aptos.Crypto/PublicKey.cs
@@ -1,7 +1,35 @@
namespace Aptos;
-public abstract class PublicKey : Serializable
+using System.Runtime.Serialization;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Linq;
+
+[JsonConverter(typeof(StringEnumConverter))]
+public enum PublicKeyVariant : uint
{
+ [EnumMember(Value = "ed25519")]
+ Ed25519,
+
+ [EnumMember(Value = "secp256k1_ecdsa")]
+ Secp256k1Ecdsa,
+
+ [EnumMember(Value = "secp256r1_ecdsa")]
+ Secp256r1Ecdsa,
+
+ [EnumMember(Value = "keyless")]
+ Keyless,
+}
+
+[JsonConverter(typeof(PublicKeyConverter))]
+public abstract class PublicKey(PublicKeyVariant type) : Serializable
+{
+ [JsonProperty("type")]
+ public readonly PublicKeyVariant Type = type;
+
+ [JsonProperty("value")]
+ public abstract Hex Value { get; }
+
public bool VerifySignature(string message, Signature signature) =>
VerifySignature(SigningMessage.Convert(message), signature);
@@ -9,5 +37,62 @@ public bool VerifySignature(string message, Signature signature) =>
public abstract byte[] ToByteArray();
+ public static PublicKey Deserialize(Deserializer d)
+ {
+ PublicKeyVariant variant = (PublicKeyVariant)d.Uleb128AsU32();
+ return variant switch
+ {
+ PublicKeyVariant.Ed25519 => new Ed25519PublicKey(d.Bytes()),
+ PublicKeyVariant.Secp256k1Ecdsa => new Secp256k1PublicKey(d.Bytes()),
+ PublicKeyVariant.Keyless => KeylessPublicKey.Deserialize(d),
+ _ => throw new ArgumentException("Invalid public key variant"),
+ };
+ }
+
public override string ToString() => Hex.FromHexInput(ToByteArray()).ToString();
}
+
+public class PublicKeyConverter : JsonConverter
+{
+ public override PublicKey? ReadJson(
+ JsonReader reader,
+ Type objectType,
+ PublicKey? existingValue,
+ bool hasExistingValue,
+ JsonSerializer serializer
+ )
+ {
+ var jsonObject = JObject.Load(reader);
+ var type = jsonObject["type"]?.ToString();
+
+ AnyValue? anyValue = JsonConvert.DeserializeObject(jsonObject.ToString());
+ if (anyValue == null)
+ throw new Exception("Invalid public key shape");
+
+ Deserializer deserializer = new(anyValue.Value);
+ deserializer.Uleb128AsU32();
+
+ return type switch
+ {
+ "ed25519" => new Ed25519PublicKey(anyValue.Value),
+ "secp256k1_ecdsa" => new Secp256k1PublicKey(anyValue.Value),
+ "keyless" => KeylessPublicKey.Deserialize(new Deserializer(anyValue.Value)),
+ _ => throw new Exception($"Unknown public key type: {type}"),
+ };
+ }
+
+ public override void WriteJson(JsonWriter writer, PublicKey? value, JsonSerializer serializer)
+ {
+ if (value == null)
+ writer.WriteNull();
+ else
+ {
+ writer.WriteStartObject();
+ writer.WritePropertyName("type");
+ writer.WriteValue(JsonConvert.SerializeObject(value.Type).Replace("\"", ""));
+ writer.WritePropertyName("value");
+ writer.WriteValue(value.Value.ToString());
+ writer.WriteEndObject();
+ }
+ }
+}
diff --git a/Aptos/Aptos.Crypto/PublicKeySignature.cs b/Aptos/Aptos.Crypto/PublicKeySignature.cs
new file mode 100644
index 0000000..c709791
--- /dev/null
+++ b/Aptos/Aptos.Crypto/PublicKeySignature.cs
@@ -0,0 +1,92 @@
+namespace Aptos;
+
+using System.Runtime.Serialization;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Linq;
+
+[JsonConverter(typeof(StringEnumConverter))]
+public enum PublicKeySignatureVariant
+{
+ [EnumMember(Value = "ed25519")]
+ Ed25519 = 0,
+
+ [EnumMember(Value = "secp256k1_ecdsa")]
+ Secp256k1Ecdsa = 1,
+
+ [EnumMember(Value = "keyless")]
+ Keyless = 3,
+}
+
+///
+/// Cryptographic signatures typically using a private key to sign a message or transaction.
+///
+[JsonConverter(typeof(PublicKeySignatureConverter))]
+public abstract class PublicKeySignature(PublicKeySignatureVariant type) : Signature
+{
+ [JsonProperty("type")]
+ public PublicKeySignatureVariant Type = type;
+
+ [JsonProperty("value")]
+ public abstract Hex Value { get; }
+
+ public abstract override byte[] ToByteArray();
+
+ public static PublicKeySignature Deserialize(Deserializer d)
+ {
+ PublicKeySignatureVariant variant = (PublicKeySignatureVariant)d.Uleb128AsU32();
+ return variant switch
+ {
+ PublicKeySignatureVariant.Ed25519 => Ed25519Signature.Deserialize(d),
+ PublicKeySignatureVariant.Secp256k1Ecdsa => Secp256k1Signature.Deserialize(d),
+ PublicKeySignatureVariant.Keyless => KeylessSignature.Deserialize(d),
+ _ => throw new ArgumentException("Invalid signature variant"),
+ };
+ }
+}
+
+public class PublicKeySignatureConverter : JsonConverter
+{
+ public override PublicKeySignature? ReadJson(
+ JsonReader reader,
+ Type objectType,
+ PublicKeySignature? existingValue,
+ bool hasExistingValue,
+ JsonSerializer serializer
+ )
+ {
+ var jsonObject = JObject.Load(reader);
+ var type = jsonObject["type"]?.ToString();
+
+ AnyValue? anyValue = JsonConvert.DeserializeObject(jsonObject.ToString());
+ if (anyValue == null)
+ throw new Exception("Invalid Signature shape");
+
+ return type switch
+ {
+ "ed25519" => new Ed25519Signature(anyValue.Value),
+ "secp256k1_ecdsa" => new Secp256k1Signature(anyValue.Value),
+ "keyless" => KeylessSignature.Deserialize(new Deserializer(anyValue.Value)),
+ _ => throw new Exception($"Unknown signature type: {type}"),
+ };
+ }
+
+ public override void WriteJson(
+ JsonWriter writer,
+ PublicKeySignature? value,
+ JsonSerializer serializer
+ )
+ {
+ if (value == null)
+ writer.WriteNull();
+ else
+ {
+ writer.WriteStartObject();
+ writer.WritePropertyName("type");
+ writer.WriteValue(JsonConvert.SerializeObject(value.Type).Replace("\"", ""));
+ writer.WritePropertyName("value");
+ writer.WriteValue(value.Value.ToString());
+ writer.WriteEndObject();
+ }
+ }
+}
diff --git a/Aptos/Aptos.Crypto/Secp256k1.cs b/Aptos/Aptos.Crypto/Secp256k1.cs
index 670b808..d4fb882 100644
--- a/Aptos/Aptos.Crypto/Secp256k1.cs
+++ b/Aptos/Aptos.Crypto/Secp256k1.cs
@@ -23,7 +23,7 @@ public static class Secp256k1
.Curve.Order.ShiftRight(1);
}
-public class Secp256k1PublicKey : LegacyPublicKey
+public class Secp256k1PublicKey : PublicKey
{
static readonly int LENGTH = 65;
@@ -65,7 +65,7 @@ public override bool VerifySignature(byte[] message, Signature signature)
return signer.VerifySignature(hash, r, s);
}
- public static Secp256k1PublicKey Deserialize(Deserializer d) => new(d.Bytes());
+ public static new Secp256k1PublicKey Deserialize(Deserializer d) => new(d.Bytes());
}
public class Secp256k1PrivateKey : PrivateKey
@@ -101,9 +101,7 @@ public override PublicKey PublicKey()
return new Secp256k1PublicKey(publicKey.Q.GetEncoded(false));
}
- public override Signature Sign(string message) => Sign(SigningMessage.Convert(message));
-
- public override Signature Sign(byte[] message)
+ public override PublicKeySignature Sign(byte[] message)
{
// Hash the message
byte[] hash = DigestUtilities.CalculateDigest("SHA3-256", message);
@@ -158,7 +156,7 @@ public static Secp256k1PrivateKey FromDerivationPath(string path, string mnemoni
public static Secp256k1PrivateKey Deserialize(Deserializer d) => new(d.Bytes());
}
-public class Secp256k1Signature : LegacySignature
+public class Secp256k1Signature : PublicKeySignature
{
static readonly int LENGTH = 64;
@@ -170,7 +168,7 @@ public Secp256k1Signature(string signature)
: this(Hex.FromHexInput(signature).ToByteArray()) { }
public Secp256k1Signature(byte[] signature)
- : base(SignatureVariant.Secp256k1Ecdsa)
+ : base(PublicKeySignatureVariant.Secp256k1Ecdsa)
{
if (signature.Length != LENGTH)
throw new KeyLengthMismatch("Secp256k1Signature", LENGTH);
@@ -181,5 +179,5 @@ public Secp256k1Signature(byte[] signature)
public override void Serialize(Serializer s) => s.Bytes(_value.ToByteArray());
- public static Secp256k1Signature Deserialize(Deserializer d) => new(d.Bytes());
+ public static new Secp256k1Signature Deserialize(Deserializer d) => new(d.Bytes());
}
diff --git a/Aptos/Aptos.Crypto/Signature.cs b/Aptos/Aptos.Crypto/Signature.cs
index d84450d..291fe2d 100644
--- a/Aptos/Aptos.Crypto/Signature.cs
+++ b/Aptos/Aptos.Crypto/Signature.cs
@@ -1,10 +1,5 @@
namespace Aptos;
-using System.Runtime.Serialization;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
-using Newtonsoft.Json.Linq;
-
///
/// Base signatures for anything signed (not specific to signing transactions/messages). This may include all signatures needed for ZK proofs, Certificates, etc.
///
@@ -14,80 +9,3 @@ public abstract class Signature : Serializable
public override string ToString() => Hex.FromHexInput(ToByteArray()).ToString();
}
-
-[JsonConverter(typeof(StringEnumConverter))]
-public enum SignatureVariant
-{
- [EnumMember(Value = "ed25519")]
- Ed25519 = 0,
-
- [EnumMember(Value = "secp256k1_ecdsa")]
- Secp256k1Ecdsa = 1,
-
- [EnumMember(Value = "keyless")]
- Keyless = 3,
-}
-
-///
-/// Signature for results of signing transactions/messages using a authentication scheme (e.g. Ed25519, Keyless, etc.)
-///
-///
-/// The type of the signature (e.g. Ed25519, Keyless, etc.)
-///
-[JsonConverter(typeof(LegacySignatureConverter))]
-public abstract class LegacySignature(SignatureVariant type) : Signature
-{
- [JsonProperty("type")]
- public SignatureVariant Type = type;
-
- [JsonProperty("value")]
- public abstract Hex Value { get; }
-}
-
-public abstract class UnifiedSignature : Signature { }
-
-public class LegacySignatureConverter : JsonConverter
-{
- public override LegacySignature? ReadJson(
- JsonReader reader,
- Type objectType,
- LegacySignature? existingValue,
- bool hasExistingValue,
- JsonSerializer serializer
- )
- {
- var jsonObject = JObject.Load(reader);
- var type = jsonObject["type"]?.ToString();
-
- AnyValue? anyValue = JsonConvert.DeserializeObject(jsonObject.ToString());
- if (anyValue == null)
- throw new Exception("Invalid LegacySignature shape");
-
- return type switch
- {
- "ed25519" => new Ed25519Signature(anyValue.Value),
- "secp256k1_ecdsa" => new Secp256k1Signature(anyValue.Value),
- "keyless" => KeylessSignature.Deserialize(new Deserializer(anyValue.Value)),
- _ => throw new Exception($"Unknown signature type: {type}"),
- };
- }
-
- public override void WriteJson(
- JsonWriter writer,
- LegacySignature? value,
- JsonSerializer serializer
- )
- {
- if (value == null)
- writer.WriteNull();
- else
- {
- writer.WriteStartObject();
- writer.WritePropertyName("type");
- writer.WriteValue(JsonConvert.SerializeObject(value.Type).Replace("\"", ""));
- writer.WritePropertyName("value");
- writer.WriteValue(value.Value.ToString());
- writer.WriteEndObject();
- }
- }
-}
diff --git a/Aptos/Aptos.Crypto/SingleKey.cs b/Aptos/Aptos.Crypto/SingleKey.cs
index fc279ff..7950ed5 100644
--- a/Aptos/Aptos.Crypto/SingleKey.cs
+++ b/Aptos/Aptos.Crypto/SingleKey.cs
@@ -1,91 +1,25 @@
-namespace Aptos;
-
using Aptos.Schemes;
-public class AnyPublicKey : UnifiedAccountPublicKey
-{
- public readonly PublicKey PublicKey;
-
- public readonly PublicKeyVariant Type;
-
- public AnyPublicKey(PublicKey publicKey)
- {
- if (publicKey is LegacyPublicKey legacyPublicKey)
- {
- PublicKey = legacyPublicKey;
- Type = legacyPublicKey.Type;
- }
- else if (publicKey is LegacyAccountPublicKey legacyAccountPublicKey)
- {
- PublicKey = legacyAccountPublicKey;
- Type = legacyAccountPublicKey.Type;
- }
- else
- {
- throw new ArgumentException("Invalid public key type");
- }
- }
-
- public override bool VerifySignature(byte[] message, Signature signature) =>
- PublicKey.VerifySignature(
- message,
- signature is AnySignature anySignature ? anySignature.Signature : signature
- );
-
- public override AuthenticationKey AuthKey() =>
- AuthenticationKey.FromSchemeAndBytes(AuthenticationKeyScheme.SingleKey, BcsToBytes());
-
- public override byte[] ToByteArray() => PublicKey.ToByteArray();
-
- public override void Serialize(Serializer s)
- {
- s.U32AsUleb128((uint)Type);
- PublicKey.Serialize(s);
- }
-
- public static AnyPublicKey Deserialize(Deserializer d)
- {
- PublicKeyVariant variant = (PublicKeyVariant)d.Uleb128AsU32();
- return variant switch
- {
- PublicKeyVariant.Ed25519 => new AnyPublicKey(Ed25519PublicKey.Deserialize(d)),
- PublicKeyVariant.Secp256k1Ecdsa => new AnyPublicKey(Secp256k1PublicKey.Deserialize(d)),
- PublicKeyVariant.Keyless => new AnyPublicKey(KeylessPublicKey.Deserialize(d)),
- _ => throw new ArgumentException("Invalid public key variant"),
- };
- }
-}
+namespace Aptos;
-public class AnySignature(LegacySignature signature) : UnifiedSignature
+public partial class SingleKey(PublicKey publicKey) : Serializable, IVerifyingKey
{
- public readonly Signature Signature = signature;
+ public readonly PublicKey PublicKey = publicKey;
- public readonly SignatureVariant Type = signature.Type;
+ public AuthenticationKey AuthKey() =>
+ AuthenticationKey.FromSchemeAndBytes(AuthenticationKeyScheme.SingleKey, BcsToBytes());
- public AnySignature(Signature signature)
- : this(
- signature is LegacySignature accountSignature
- ? accountSignature
- : throw new ArgumentException("Invalid signature type")
- ) { }
+ public bool VerifySignature(string message, Signature signature) =>
+ VerifySignature(SigningMessage.Convert(message), signature);
- public override byte[] ToByteArray() => Signature.ToByteArray();
+ public bool VerifySignature(byte[] message, Signature signature) =>
+ PublicKey.VerifySignature(message, signature);
public override void Serialize(Serializer s)
{
- s.U32AsUleb128((uint)Type);
- Signature.Serialize(s);
+ s.U32AsUleb128((uint)PublicKey.Type);
+ PublicKey.Serialize(s);
}
- public static AnySignature Deserialize(Deserializer d)
- {
- SignatureVariant variant = (SignatureVariant)d.Uleb128AsU32();
- return variant switch
- {
- SignatureVariant.Ed25519 => new AnySignature(Ed25519Signature.Deserialize(d)),
- SignatureVariant.Secp256k1Ecdsa => new AnySignature(Secp256k1Signature.Deserialize(d)),
- SignatureVariant.Keyless => new AnySignature(KeylessSignature.Deserialize(d)),
- _ => throw new ArgumentException("Invalid signature variant"),
- };
- }
+ public static SingleKey Deserialize(Deserializer d) => new(PublicKey.Deserialize(d));
}
diff --git a/Aptos/Aptos.Exceptions/Crypto.cs b/Aptos/Aptos.Exceptions/Crypto.cs
index b011c70..1de6efb 100644
--- a/Aptos/Aptos.Exceptions/Crypto.cs
+++ b/Aptos/Aptos.Exceptions/Crypto.cs
@@ -1,6 +1,6 @@
namespace Aptos.Exceptions
{
- public class EphemeralSignatureVariantUnsupported(SignatureVariant variant)
+ public class EphemeralSignatureVariantUnsupported(PublicKeySignatureVariant variant)
: BaseException($"Ephemeral signature variant {variant} is not supported") { }
public class EphemeralKeyVariantUnsupported(PublicKeyVariant variant)
diff --git a/Aptos/Aptos.Transactions/Authenticator/AccountAuthenticatorSingleKey.cs b/Aptos/Aptos.Transactions/Authenticator/AccountAuthenticatorSingleKey.cs
index b3e703f..c8dc753 100644
--- a/Aptos/Aptos.Transactions/Authenticator/AccountAuthenticatorSingleKey.cs
+++ b/Aptos/Aptos.Transactions/Authenticator/AccountAuthenticatorSingleKey.cs
@@ -1,23 +1,27 @@
namespace Aptos;
-public class AccountAuthenticatorSingleKey(AnyPublicKey publicKey, AnySignature signature)
+public class AccountAuthenticatorSingleKey(PublicKey publicKey, PublicKeySignature signature)
: AccountAuthenticator
{
- public readonly AnyPublicKey PublicKey = publicKey;
+ public readonly PublicKey PublicKey = publicKey;
- public readonly AnySignature Signature = signature;
+ public readonly PublicKeySignature Signature = signature;
public override void Serialize(Serializer s)
{
s.U32AsUleb128((uint)AccountAuthenticatorVariant.SingleKey);
+
+ s.U32AsUleb128((uint)PublicKey.Type);
PublicKey.Serialize(s);
+
+ s.U32AsUleb128((uint)Signature.Type);
Signature.Serialize(s);
}
public static new AccountAuthenticatorSingleKey Deserialize(Deserializer d)
{
- AnyPublicKey publicKey = AnyPublicKey.Deserialize(d);
- AnySignature signature = AnySignature.Deserialize(d);
+ PublicKey publicKey = PublicKey.Deserialize(d);
+ PublicKeySignature signature = PublicKeySignature.Deserialize(d);
return new AccountAuthenticatorSingleKey(publicKey, signature);
}
}
diff --git a/Aptos/Aptos.Transactions/TransactionBuilder.cs b/Aptos/Aptos.Transactions/TransactionBuilder.cs
index 392d10f..310ad94 100644
--- a/Aptos/Aptos.Transactions/TransactionBuilder.cs
+++ b/Aptos/Aptos.Transactions/TransactionBuilder.cs
@@ -16,18 +16,15 @@ public static AccountAuthenticator GetAuthenticatorForSimulation(PublicKey publi
return new AccountAuthenticatorEd25519(ed25519PublicKey, invalidSignature);
}
- if (publicKey is AnyPublicKey anyPublicKey)
+ if (publicKey is KeylessPublicKey keylessPublicKey)
{
return new AccountAuthenticatorSingleKey(
- anyPublicKey,
- new AnySignature(invalidSignature)
+ keylessPublicKey,
+ Keyless.GetSimulationSignature()
);
}
- throw new InvalidPublicKey(
- publicKey,
- "Authenticator for simulation cannot be derived for this PublicKey."
- );
+ return new AccountAuthenticatorSingleKey(publicKey, invalidSignature);
}
#endregion
@@ -50,10 +47,7 @@ public static SignedTransaction GeneratedSignedTransaction(SubmitTransactionData
(data.Transaction.FeePayerAddress, data.FeePayerAuthenticator)
);
}
- else if (
- data.Transaction.SecondarySignerAddresses != null
- || data.SenderAuthenticator is AccountAuthenticatorMultiKey
- )
+ else if (data.Transaction.SecondarySignerAddresses != null)
{
// Make sure that there are enough additional authenticators for the secondary signers addresses
if (
@@ -82,6 +76,12 @@ public static SignedTransaction GeneratedSignedTransaction(SubmitTransactionData
singleKeyAuthenticator
);
}
+ else if (data.SenderAuthenticator is AccountAuthenticatorMultiKey multiKeyAuthenticator)
+ {
+ transactionAuthenticator = new TransactionAuthenticatorSingleSender(
+ multiKeyAuthenticator
+ );
+ }
if (transactionAuthenticator == null)
throw new ArgumentException("Invalid authentication scheme");
diff --git a/features/single_key.feature b/features/single_key.feature
new file mode 100644
index 0000000..72cd992
--- /dev/null
+++ b/features/single_key.feature
@@ -0,0 +1,42 @@
+Feature: SingleKey
+"""
+Single is a verification key used to authenticate a public key. This specification aims to covers SingleKey signatures, serialization, and authentication keys.
+
+All key values are represented in BCS (Binary Canonical Serialization) format as a Hex string. It's important to use a Deserializer ensure compatibility.
+"""
+
+ Scenario Outline: SingleKey serialization
+ Given
+ When I serialize
+ Then the result should be bcs
+
+ Examples:
+ | type | key | value |
+ | ed25519 | 0x2066e6dde4a271c5fcc68e9bda668ac41ff67049ea75e4db103c7927934b1e4ace | 0x002066e6dde4a271c5fcc68e9bda668ac41ff67049ea75e4db103c7927934b1e4ace |
+ | secp256k1 | 0x4104ffaf07a5268f92f86aa8be6a8047aaf2cceb33c3ac69628d9dde4e2ffdbe2d21551ecb9f69a6a79a509a7328a6641d81b4bb1fb39d4955549dad93d44ccc9d50 | 0x014104ffaf07a5268f92f86aa8be6a8047aaf2cceb33c3ac69628d9dde4e2ffdbe2d21551ecb9f69a6a79a509a7328a6641d81b4bb1fb39d4955549dad93d44ccc9d50 |
+ | keyless | 0x1b68747470733a2f2f6163636f756e74732e676f6f676c652e636f6d20efbfb94b89da565468e11b1a7d29eb3be1032637ec8d21b61f5220511d668814 | 0x031b68747470733a2f2f6163636f756e74732e676f6f676c652e636f6d20efbfb94b89da565468e11b1a7d29eb3be1032637ec8d21b61f5220511d668814 |
+
+ Scenario Outline: SingleKey authentication key
+ Given
+ When I derive authentication key
+ Then the result should be bcs
+
+ Examples:
+ | type | key | value |
+ | ed25519 | 0x2066e6dde4a271c5fcc68e9bda668ac41ff67049ea75e4db103c7927934b1e4ace | 0x73312812b63e652652f5a2aecfcb42ec051923c3456329d9500eb0e919e3903d |
+ | secp256k1 | 0x4104ffaf07a5268f92f86aa8be6a8047aaf2cceb33c3ac69628d9dde4e2ffdbe2d21551ecb9f69a6a79a509a7328a6641d81b4bb1fb39d4955549dad93d44ccc9d50 | 0xe6e936c0c9c41d8c1f0490de291789677d64467ef38f1fff7111cad9546eee44 |
+ | keyless | 0x1b68747470733a2f2f6163636f756e74732e676f6f676c652e636f6d20efbfb94b89da565468e11b1a7d29eb3be1032637ec8d21b61f5220511d668814 | 0x3c38e3250bf5e446947a9fd4bdfd41e76c3812e8061abf40702febde99b68d51 |
+
+ Scenario Outline: Verify signatures using SingleKey
+ Given
+ When I verify signature with message
+ Then the result should be bool
+
+ Examples:
+ | type | key | signature | message | value |
+ | ed25519 | 0x2066e6dde4a271c5fcc68e9bda668ac41ff67049ea75e4db103c7927934b1e4ace | 0x40400461806f41581b75459e5db432e9d4da0881ac0ef0b7f8bb1c4b8e108d4ec2dd88abf5ff1e00616b2c338c375e245470e15d75f69aec465fa090001df19505 | hello world | true |
+ | ed25519 | 0x2066e6dde4a271c5fcc68e9bda668ac41ff67049ea75e4db103c7927934b1e4ace | 0x40400461806f41581b75459e5db432e9d4da0881ac0ef0b7f8bb1c4b8e108d4ec2dd88abf5ff1e00616b2c338c375e245470e15d75f69aec465fa090001df19505 | N/A | false |
+ | secp256k1 | 0x4104ffaf07a5268f92f86aa8be6a8047aaf2cceb33c3ac69628d9dde4e2ffdbe2d21551ecb9f69a6a79a509a7328a6641d81b4bb1fb39d4955549dad93d44ccc9d50 | 0x40882b4ddbc14df439c222300c051d1039a62d36026ecd5789fd777224fef0ab7e07bb0891c5b411ec0938456a4b250067e8ff6c996d31f0186019521fc8f6fb25 | hello world | true |
+ | secp256k1 | 0x4104ffaf07a5268f92f86aa8be6a8047aaf2cceb33c3ac69628d9dde4e2ffdbe2d21551ecb9f69a6a79a509a7328a6641d81b4bb1fb39d4955549dad93d44ccc9d50 | 0x40882b4ddbc14df439c222300c051d1039a62d36026ecd5789fd777224fef0ab7e07bb0891c5b411ec0938456a4b250067e8ff6c996d31f0186019521fc8f6fb25 | N/A | false |
+ | keyless | 0x1b68747470733a2f2f6163636f756e74732e676f6f676c652e636f6d20efbfb94b89da565468e11b1a7d29eb3be1032637ec8d21b61f5220511d668814 | 0x000016e4ab03e4a1f199b26b32ecc1ad3f7d8676ed332eef87f29f1198582366af88a91fcb2cfd0ac6599bd720e8815de8a288cabd5c21706a7d675e80365e2e6f1056c753e59d1d597b41ac34e5db0334982bfd2fc1c8cc167edb866b9e78c0eda96155be3d53755fdc7922028d789546b3929b3c50cfdd290d86b3eae5b45c070f809698000000000000000100405f4891933c8ac11391f82a2ef6c51131da95c3cc56e48daca75e2153c13faa48050f893fb97d72719ef1a7872c0c5f164a670c0b8325b5608d3cbcca7d2ed20d4c7b22616c67223a225253323536222c226b6964223a2262323632306435653766313332623532616665383837356364663337373663303634323439643034222c22747970223a224a5754227dc03d04670000000000206c2adb07d4bdf1462fa4090578805f654c00d5316b600f2ac7b7a92f727016f6004063b9bd27870d78845daa78effc11fc0acbadaf377a5df2791fe45f4ea0d9f10ea267b73692e7870fce3d37288631b691baf920bef64cba2996259cbabf419600 | hello world | true |
+ | keyless | 0x1b68747470733a2f2f6163636f756e74732e676f6f676c652e636f6d20efbfb94b89da565468e11b1a7d29eb3be1032637ec8d21b61f5220511d668814 | 0x000016e4ab03e4a1f199b26b32ecc1ad3f7d8676ed332eef87f29f1198582366af88a91fcb2cfd0ac6599bd720e8815de8a288cabd5c21706a7d675e80365e2e6f1056c753e59d1d597b41ac34e5db0334982bfd2fc1c8cc167edb866b9e78c0eda96155be3d53755fdc7922028d789546b3929b3c50cfdd290d86b3eae5b45c070f809698000000000000000100405f4891933c8ac11391f82a2ef6c51131da95c3cc56e48daca75e2153c13faa48050f893fb97d72719ef1a7872c0c5f164a670c0b8325b5608d3cbcca7d2ed20d4c7b22616c67223a225253323536222c226b6964223a2262323632306435653766313332623532616665383837356364663337373663303634323439643034222c22747970223a224a5754227dc03d04670000000000206c2adb07d4bdf1462fa4090578805f654c00d5316b600f2ac7b7a92f727016f6004063b9bd27870d78845daa78effc11fc0acbadaf377a5df2791fe45f4ea0d9f10ea267b73692e7870fce3d37288631b691baf920bef64cba2996259cbabf419600 | N/A | false |