Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements deserialization for Multi Signature Accounts #289

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SharedBuildProperties.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Product>Solnet</Product>
<Version>0.4.17</Version>
<Version>0.4.18</Version>
<Copyright>Copyright 2021 &#169; Solnet</Copyright>
<Authors>blockmountain</Authors>
<PublisherName>blockmountain</PublisherName>
Expand Down
25 changes: 25 additions & 0 deletions src/Solnet.Examples/MultisigExamples.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Solnet.Programs;
using Solnet.Programs.Models;
using Solnet.Rpc;
using Solnet.Rpc.Builders;
using Solnet.Rpc.Core.Http;
Expand Down Expand Up @@ -1021,4 +1022,28 @@ public void Run()
}
}

public class GetMultiSignatureAccountExample : IExample
{
private static readonly IRpcClient rpcClient = ClientFactory.GetClient(Cluster.TestNet);

private const string MnemonicWords =
"route clerk disease box emerge airport loud waste attitude film army tray " +
"forward deal onion eight catalog surface unit card window walnut wealth medal";


public void Run()
{
Wallet.Wallet wallet = new(MnemonicWords);

// The multisig which is the token account authority
Account tokenMultiSignature = wallet.GetAccount(4045);

var account = rpcClient.GetAccountInfo(tokenMultiSignature.PublicKey);

var multiSigAccount = MultiSignatureAccount.Deserialize(Convert.FromBase64String(account.Result.Value.Data[0]));


}
}

}
94 changes: 94 additions & 0 deletions src/Solnet.Programs/Models/MultiSignatureAccount.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using Solnet.Programs.Utilities;
using Solnet.Wallet;
using System;
using System.Collections.Generic;

namespace Solnet.Programs.Models
{
/// <summary>
/// Represents a <see cref="TokenProgram"/> Multi Signature Account in Solana.
/// </summary>
public class MultiSignatureAccount
{
/// <summary>
/// The maximum number of signers.
/// </summary>
public const int MaxSigners = 11;

/// <summary>
/// The layout of the <see cref="MultiSignatureAccount"/> structure.
/// </summary>
public static class Layout
{
/// <summary>
/// The length of the structure.
/// </summary>
public const int Length = 355;

/// <summary>
/// The offset at which the number of signers required value begins.
/// </summary>
public const int MinimumSignersOffset = 0;

/// <summary>
/// The offset at which the number of valid signers value begins.
/// </summary>
public const int NumberSignersOffset = 1;

/// <summary>
/// The offset at which the is initialized value begins.
/// </summary>
public const int IsInitializedOffset = 2;

/// <summary>
/// The offset at which the array with signers' public keys begins.
/// </summary>
public const int SignersOffset = 3;
}

/// <summary>
/// Number of signers required
/// </summary>
public byte MinimumSigners;

/// <summary>
/// Number of valid signers
/// </summary>
public byte NumberSigners;

/// <summary>
/// Whether the account has been initialized
/// </summary>
public bool IsInitialized;

/// <summary>
/// Signer public keys
/// </summary>
public List<PublicKey> Signers;

/// <summary>
/// Deserialize the given data into the <see cref="MultiSignatureAccount"/> structure.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>The <see cref="MultiSignatureAccount"/> structure.</returns>
public static MultiSignatureAccount Deserialize(ReadOnlySpan<byte> data)
{
List<PublicKey> signers = new();

for(int i= 0; i < MaxSigners; i++)
{
var signer = data.GetPubKey(Layout.SignersOffset + i * PublicKey.PublicKeyLength);
if (signer != SystemProgram.ProgramIdKey)
signers.Add(signer);
}

return new MultiSignatureAccount
{
MinimumSigners = data.GetU8(Layout.MinimumSignersOffset),
NumberSigners = data.GetU8(Layout.NumberSignersOffset),
IsInitialized = data.GetBool(Layout.IsInitializedOffset),
Signers = signers
};
}
}
}
26 changes: 26 additions & 0 deletions test/Solnet.Programs.Test/TokenProgramTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Solnet.Programs.Models;
using Solnet.Rpc.Models;
using Solnet.Wallet;
using System;
Expand Down Expand Up @@ -221,6 +222,15 @@ public class TokenProgramTest
"AKkFSlNQ+F3IgtYUpVZyeIopbd8eq6vQpgZ4iEky9O72oBsCrLfKwExmcW/hntBXRIKAe6vTrQDRoyz2ZvGtaL" +
"7sAwcGBAUGAQIDCg/gnyZ3AAAAAAoHBgQABgECAwEJCAEAEkhlbGxvIGZyb20gU29sLk5ldA==";

private const string MultiSignatureAccountBase64Data =
"AwUBWM8dG26h12WDYbEd7sD1a0xQDEwtI8e9q6oVqCKB5sBp6kQNrCn8mBZqa417ZDd6Nqx1cIFeDdEbQal0JhI" +
"K6ENGzSoCrq8zjWkLr7j6eNDY9ksF0JtCJBIRRUkQzsD+rq/O6gag1j7CDsONdF6cGtgzee/vw3I1Ld78u6n8Hz" +
"XqSQFQFFq2MzZJ+APbduagMeovWpJoxRmd6QoIz1n72gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";


[TestMethod]
public void TestTransfer()
{
Expand Down Expand Up @@ -1415,5 +1425,21 @@ public void DecodeBurnCheckedAndCloseMultisigTest()
Assert.AreEqual("88SzfLipgVTvi8hQwYfq21DgQFcABx6yAwgJH5shfqVZ", (PublicKey)signer2);
Assert.AreEqual("5Xcw7EQb6msgpVdGB8Hf8kpCqVyacTChgFBUphpuUeBo", (PublicKey)signer3);
}

[TestMethod]
public void TestMultiSignatureAccountDeserialization()
{
var multiSigAccount = MultiSignatureAccount.Deserialize(Convert.FromBase64String(MultiSignatureAccountBase64Data));

Assert.AreEqual(3, multiSigAccount.MinimumSigners);
Assert.AreEqual(5, multiSigAccount.NumberSigners);
Assert.IsTrue(multiSigAccount.IsInitialized);
Assert.AreEqual(5, multiSigAccount.Signers.Count);
Assert.AreEqual("6yg3tZM1szHj752RDxQ1GxwvkzR3GyuvAcH498ew1t2T", multiSigAccount.Signers[0].Key);
Assert.AreEqual("88SzfLipgVTvi8hQwYfq21DgQFcABx6yAwgJH5shfqVZ", multiSigAccount.Signers[1].Key);
Assert.AreEqual("5Xcw7EQb6msgpVdGB8Hf8kpCqVyacTChgFBUphpuUeBo", multiSigAccount.Signers[2].Key);
Assert.AreEqual("CkuRf85gy9Q2733Hi5bFFuznpWjn19XzhJQQyz8LTaMi", multiSigAccount.Signers[3].Key);
Assert.AreEqual("GmYy7Gkhkz4DWsA4RpCZoLS8UXpv8iZzTAESziYgRBEq", multiSigAccount.Signers[4].Key);
}
}
}