Skip to content

Commit 012e10e

Browse files
committed
Added Versioned Transactions to RPC client. Read/Send
Refactored from Unity core patch
1 parent 58ec347 commit 012e10e

7 files changed

+588
-13
lines changed

src/Solnet.Rpc/Builders/MessageBuilder.cs

+9-10
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,22 @@ public class MessageBuilder
1818
/// <summary>
1919
/// The length of the block hash.
2020
/// </summary>
21-
private const int BlockHashLength = 32;
21+
protected const int BlockHashLength = 32;
2222

2323
/// <summary>
2424
/// The message header.
2525
/// </summary>
26-
private MessageHeader _messageHeader;
26+
protected MessageHeader _messageHeader;
2727

2828
/// <summary>
2929
/// The account keys list.
3030
/// </summary>
31-
private readonly AccountKeysList _accountKeysList;
31+
protected readonly AccountKeysList _accountKeysList;
3232

3333
/// <summary>
3434
/// The list of instructions contained within this transaction.
3535
/// </summary>
36-
internal List<TransactionInstruction> Instructions { get; private set; }
36+
internal List<TransactionInstruction> Instructions { get; private protected set; }
3737

3838
/// <summary>
3939
/// The hash of a recent block.
@@ -76,7 +76,7 @@ internal MessageBuilder AddInstruction(TransactionInstruction instruction)
7676
/// Builds the message into the wire format.
7777
/// </summary>
7878
/// <returns>The encoded message.</returns>
79-
internal byte[] Build()
79+
internal virtual byte[] Build()
8080
{
8181
if (RecentBlockHash == null && NonceInformation == null)
8282
throw new Exception("recent block hash or nonce information is required");
@@ -111,8 +111,7 @@ internal byte[] Build()
111111
{
112112
keyIndices[i] = FindAccountIndex(keysList, instruction.Keys[i].PublicKey);
113113
}
114-
115-
CompiledInstruction compiledInstruction = new CompiledInstruction
114+
CompiledInstruction compiledInstruction = new()
116115
{
117116
ProgramIdIndex = FindAccountIndex(keysList, instruction.ProgramId),
118117
KeyIndicesCount = ShortVectorEncoding.EncodeLength(keyCount),
@@ -176,7 +175,7 @@ internal byte[] Build()
176175
/// Gets the keys for the accounts present in the message.
177176
/// </summary>
178177
/// <returns>The list of <see cref="AccountMeta"/>.</returns>
179-
private List<AccountMeta> GetAccountKeys()
178+
protected List<AccountMeta> GetAccountKeys()
180179
{
181180
List<AccountMeta> newList = new();
182181
var keysList = _accountKeysList.AccountList;
@@ -203,7 +202,7 @@ private List<AccountMeta> GetAccountKeys()
203202
/// <param name="accountMetas">The <see cref="AccountMeta"/>.</param>
204203
/// <param name="publicKey">The public key.</param>
205204
/// <returns>The index of the</returns>
206-
private static byte FindAccountIndex(IList<AccountMeta> accountMetas, byte[] publicKey)
205+
protected static byte FindAccountIndex(IList<AccountMeta> accountMetas, byte[] publicKey)
207206
{
208207
string encodedKey = Encoders.Base58.EncodeData(publicKey);
209208
return FindAccountIndex(accountMetas, encodedKey);
@@ -215,7 +214,7 @@ private static byte FindAccountIndex(IList<AccountMeta> accountMetas, byte[] pub
215214
/// <param name="accountMetas">The <see cref="AccountMeta"/>.</param>
216215
/// <param name="publicKey">The public key.</param>
217216
/// <returns>The index of the</returns>
218-
private static byte FindAccountIndex(IList<AccountMeta> accountMetas, string publicKey)
217+
protected static byte FindAccountIndex(IList<AccountMeta> accountMetas, string publicKey)
219218
{
220219
for (byte index = 0; index < accountMetas.Count; index++)
221220
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
using Solnet.Rpc.Models;
2+
using Solnet.Rpc.Utilities;
3+
using Solnet.Wallet;
4+
using Solnet.Wallet.Utilities;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Linq;
9+
using static Solnet.Rpc.Models.Message;
10+
11+
namespace Solnet.Rpc.Builders
12+
{
13+
/// <summary>
14+
/// A compiled instruction within the message.
15+
/// </summary>
16+
public class VersionedMessageBuilder : MessageBuilder
17+
{
18+
19+
/// <summary>
20+
/// Address Table Lookups
21+
/// </summary>
22+
public List<MessageAddressTableLookup> AddressTableLookups { get; set; }
23+
public IList<PublicKey> AccountKeys { get; internal set; }
24+
25+
/// <summary>
26+
/// Builds the message into the wire format.
27+
/// </summary>
28+
/// <returns>The encoded message.</returns>
29+
internal override byte[] Build()
30+
{
31+
if (RecentBlockHash == null && NonceInformation == null)
32+
throw new Exception("recent block hash or nonce information is required");
33+
if (Instructions == null)
34+
throw new Exception("no instructions provided in the transaction");
35+
36+
// In case the user specified nonce information, we'll use it.
37+
if (NonceInformation != null)
38+
{
39+
RecentBlockHash = NonceInformation.Nonce;
40+
_accountKeysList.Add(NonceInformation.Instruction.Keys);
41+
_accountKeysList.Add(AccountMeta.ReadOnly(new PublicKey(NonceInformation.Instruction.ProgramId),
42+
false));
43+
List<TransactionInstruction> newInstructions = new() { NonceInformation.Instruction };
44+
newInstructions.AddRange(Instructions);
45+
Instructions = newInstructions;
46+
}
47+
48+
_messageHeader = new MessageHeader();
49+
50+
List<AccountMeta> keysList = GetAccountKeys();
51+
byte[] accountAddressesLength = ShortVectorEncoding.EncodeLength(keysList.Count);
52+
int compiledInstructionsLength = 0;
53+
List<CompiledInstruction> compiledInstructions = new();
54+
55+
foreach (TransactionInstruction instruction in Instructions)
56+
{
57+
int keyCount = instruction.Keys.Count;
58+
byte[] keyIndices = new byte[keyCount];
59+
60+
if (instruction.GetType() == typeof(VersionedTransactionInstruction))
61+
{
62+
keyIndices = ((VersionedTransactionInstruction)instruction).KeyIndices;
63+
}
64+
else
65+
{
66+
for (int i = 0; i < keyCount; i++)
67+
{
68+
keyIndices[i] = FindAccountIndex(keysList, instruction.Keys[i].PublicKey);
69+
}
70+
}
71+
72+
CompiledInstruction compiledInstruction = new()
73+
{
74+
ProgramIdIndex = FindAccountIndex(keysList, instruction.ProgramId),
75+
KeyIndicesCount = ShortVectorEncoding.EncodeLength(keyIndices.Length),
76+
KeyIndices = keyIndices,
77+
DataLength = ShortVectorEncoding.EncodeLength(instruction.Data.Length),
78+
Data = instruction.Data
79+
};
80+
compiledInstructions.Add(compiledInstruction);
81+
compiledInstructionsLength += compiledInstruction.Length();
82+
}
83+
84+
int accountKeysBufferSize = _accountKeysList.AccountList.Count * 32;
85+
MemoryStream accountKeysBuffer = new MemoryStream(accountKeysBufferSize);
86+
byte[] instructionsLength = ShortVectorEncoding.EncodeLength(compiledInstructions.Count);
87+
88+
foreach (AccountMeta accountMeta in keysList)
89+
{
90+
accountKeysBuffer.Write(accountMeta.PublicKeyBytes, 0, accountMeta.PublicKeyBytes.Length);
91+
if (accountMeta.IsSigner)
92+
{
93+
_messageHeader.RequiredSignatures += 1;
94+
if (!accountMeta.IsWritable)
95+
_messageHeader.ReadOnlySignedAccounts += 1;
96+
}
97+
else
98+
{
99+
if (!accountMeta.IsWritable)
100+
_messageHeader.ReadOnlyUnsignedAccounts += 1;
101+
}
102+
}
103+
104+
#region Build Message Body
105+
106+
int messageBufferSize = MessageHeader.Layout.HeaderLength + BlockHashLength +
107+
accountAddressesLength.Length +
108+
+instructionsLength.Length + compiledInstructionsLength + accountKeysBufferSize;
109+
MemoryStream buffer = new MemoryStream(messageBufferSize);
110+
byte[] messageHeaderBytes = _messageHeader.ToBytes();
111+
112+
buffer.Write(new byte[] { 128 }, 0, 1);
113+
buffer.Write(messageHeaderBytes, 0, messageHeaderBytes.Length);
114+
buffer.Write(accountAddressesLength, 0, accountAddressesLength.Length);
115+
buffer.Write(accountKeysBuffer.ToArray(), 0, accountKeysBuffer.ToArray().Length);
116+
var encodedRecentBlockHash = Encoders.Base58.DecodeData(RecentBlockHash);
117+
buffer.Write(encodedRecentBlockHash, 0, encodedRecentBlockHash.Length);
118+
buffer.Write(instructionsLength, 0, instructionsLength.Length);
119+
120+
foreach (CompiledInstruction compiledInstruction in compiledInstructions)
121+
{
122+
buffer.WriteByte(compiledInstruction.ProgramIdIndex);
123+
buffer.Write(compiledInstruction.KeyIndicesCount, 0, compiledInstruction.KeyIndicesCount.Length);
124+
buffer.Write(compiledInstruction.KeyIndices, 0, compiledInstruction.KeyIndices.Length);
125+
buffer.Write(compiledInstruction.DataLength, 0, compiledInstruction.DataLength.Length);
126+
buffer.Write(compiledInstruction.Data, 0, compiledInstruction.Data.Length);
127+
}
128+
129+
#endregion
130+
131+
var serializeAddressTableLookups = AddressTableLookupUtils.SerializeAddressTableLookups(AddressTableLookups);
132+
buffer.Write(serializeAddressTableLookups, 0, serializeAddressTableLookups.Length);
133+
134+
return buffer.ToArray();
135+
}
136+
}
137+
}

src/Solnet.Rpc/Models/AccountKeysList.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Solnet.Rpc.Models
77
/// A wrapper around a list of <see cref="AccountMeta"/>s that takes care of deduplication and ordering according to
88
/// the wire format specification.
99
/// </summary>
10-
internal class AccountKeysList
10+
public class AccountKeysList
1111
{
1212
/// <summary>
1313
/// The account metas list.

0 commit comments

Comments
 (0)