@@ -20,17 +20,32 @@ const (
20
20
type Signature [ed25519 .SignatureSize ]byte
21
21
type Blockhash [sha256 .Size ]byte
22
22
23
+ type MessageVersion uint8
24
+
25
+ const (
26
+ MessageVersionLegacy MessageVersion = iota
27
+ MessageVersion0
28
+ )
29
+
23
30
type Header struct {
24
31
NumSignatures byte
25
32
NumReadonlySigned byte
26
33
NumReadOnly byte
27
34
}
28
35
36
+ type MessageAddressTableLookup struct {
37
+ PublicKey ed25519.PublicKey
38
+ WritableIndexes []byte
39
+ ReadonlyIndexes []byte
40
+ }
41
+
29
42
type Message struct {
30
- Header Header
31
- Accounts []ed25519.PublicKey
32
- RecentBlockhash Blockhash
33
- Instructions []CompiledInstruction
43
+ version MessageVersion
44
+ Header Header
45
+ Accounts []ed25519.PublicKey
46
+ RecentBlockhash Blockhash
47
+ Instructions []CompiledInstruction
48
+ AddressTableLookups []MessageAddressTableLookup
34
49
}
35
50
36
51
type Transaction struct {
@@ -39,6 +54,15 @@ type Transaction struct {
39
54
}
40
55
41
56
func NewTransaction (payer ed25519.PublicKey , instructions ... Instruction ) Transaction {
57
+ return newTransaction (payer , nil , instructions )
58
+ }
59
+
60
+ func NewVersionedTransaction (payer ed25519.PublicKey , addressLookupTables []AddressLookupTable , instructions []Instruction ) Transaction {
61
+ return newTransaction (payer , addressLookupTables , instructions )
62
+ }
63
+
64
+ // todo: consolidate to new constructor
65
+ func newTransaction (payer ed25519.PublicKey , addressLookupTables []AddressLookupTable , instructions []Instruction ) Transaction {
42
66
accounts := []AccountMeta {
43
67
{
44
68
PublicKey : payer ,
@@ -65,8 +89,45 @@ func NewTransaction(payer ed25519.PublicKey, instructions ...Instruction) Transa
65
89
accounts = filterUnique (accounts )
66
90
sort .Sort (SortableAccountMeta (accounts ))
67
91
92
+ // Sort address tables to guarantee consistent marshalling
93
+ sortedAddressLookupTables := make ([]AddressLookupTable , len (addressLookupTables ))
94
+ copy (sortedAddressLookupTables , addressLookupTables )
95
+ sort .Sort (SortableAddressLookupTables (sortedAddressLookupTables ))
96
+
97
+ writableAddressTableIndexes := make ([][]byte , len (sortedAddressLookupTables ))
98
+ readonlyAddressTableIndexes := make ([][]byte , len (sortedAddressLookupTables ))
99
+
68
100
var m Message
69
101
for _ , account := range accounts {
102
+ // If the account is eligible for dynamic loading, then pull its index
103
+ // from the first address table where it's defined.
104
+ var isDynamicallyLoaded bool
105
+ if ! account .isPayer && ! account .IsSigner && ! account .isProgram {
106
+ for i , addressLookupTable := range sortedAddressLookupTables {
107
+ for j , address := range addressLookupTable .Addresses {
108
+ if bytes .Equal (address , account .PublicKey ) {
109
+ isDynamicallyLoaded = true
110
+
111
+ if account .IsWritable {
112
+ writableAddressTableIndexes [i ] = append (writableAddressTableIndexes [i ], byte (j ))
113
+ } else {
114
+ readonlyAddressTableIndexes [i ] = append (readonlyAddressTableIndexes [i ], byte (j ))
115
+ }
116
+
117
+ break
118
+ }
119
+ }
120
+
121
+ if isDynamicallyLoaded {
122
+ break
123
+ }
124
+ }
125
+ }
126
+ if isDynamicallyLoaded {
127
+ continue
128
+ }
129
+
130
+ // Otherwise, the account is defined statically
70
131
m .Accounts = append (m .Accounts , account .PublicKey )
71
132
72
133
if account .IsSigner {
@@ -80,21 +141,58 @@ func NewTransaction(payer ed25519.PublicKey, instructions ...Instruction) Transa
80
141
}
81
142
}
82
143
144
+ // Consolidate static and dynamically loaded accounts into an ordered list,
145
+ // which is used for index references encoded in the message
146
+ dynamicWritableAccounts := make ([]ed25519.PublicKey , 0 )
147
+ dynamicReadonlyAccount := make ([]ed25519.PublicKey , 0 )
148
+ for i , writableAddressTableIndexes := range writableAddressTableIndexes {
149
+ for _ , index := range writableAddressTableIndexes {
150
+ writableAccount := sortedAddressLookupTables [i ].Addresses [index ]
151
+ dynamicWritableAccounts = append (dynamicWritableAccounts , writableAccount )
152
+ }
153
+ }
154
+ for i , readonlyAddressTableIndexes := range readonlyAddressTableIndexes {
155
+ for _ , index := range readonlyAddressTableIndexes {
156
+ readonlyAccount := sortedAddressLookupTables [i ].Addresses [index ]
157
+ dynamicReadonlyAccount = append (dynamicReadonlyAccount , readonlyAccount )
158
+ }
159
+ }
160
+ var allAccounts []ed25519.PublicKey
161
+ allAccounts = append (allAccounts , m .Accounts ... )
162
+ allAccounts = append (allAccounts , dynamicWritableAccounts ... )
163
+ allAccounts = append (allAccounts , dynamicReadonlyAccount ... )
164
+
83
165
// Generate the compiled instruction, which uses indices instead
84
166
// of raw account keys.
85
167
for _ , i := range instructions {
86
168
c := CompiledInstruction {
87
- ProgramIndex : byte (indexOf (m . Accounts , i .Program )),
169
+ ProgramIndex : byte (indexOf (allAccounts , i .Program )),
88
170
Data : i .Data ,
89
171
}
90
172
91
173
for _ , a := range i .Accounts {
92
- c .Accounts = append (c .Accounts , byte (indexOf (m . Accounts , a .PublicKey )))
174
+ c .Accounts = append (c .Accounts , byte (indexOf (allAccounts , a .PublicKey )))
93
175
}
94
176
95
177
m .Instructions = append (m .Instructions , c )
96
178
}
97
179
180
+ // Generate the compiled message address table lookups
181
+ for i , addressLookupTable := range sortedAddressLookupTables {
182
+ if len (writableAddressTableIndexes [i ]) == 0 && len (readonlyAddressTableIndexes [i ]) == 0 {
183
+ continue
184
+ }
185
+
186
+ m .AddressTableLookups = append (m .AddressTableLookups , MessageAddressTableLookup {
187
+ PublicKey : addressLookupTable .PublicKey ,
188
+ WritableIndexes : writableAddressTableIndexes [i ],
189
+ ReadonlyIndexes : readonlyAddressTableIndexes [i ],
190
+ })
191
+ }
192
+ if len (m .AddressTableLookups ) > 0 {
193
+ m .version = MessageVersion0
194
+ }
195
+
98
196
for i := range m .Accounts {
99
197
if len (m .Accounts [i ]) == 0 {
100
198
m .Accounts [i ] = make ([]byte , ed25519 .PublicKeySize )
@@ -118,11 +216,12 @@ func (t *Transaction) String() string {
118
216
sb .WriteString (fmt .Sprintf (" %d: %s\n " , i , base58 .Encode (s [:])))
119
217
}
120
218
sb .WriteString ("Message:\n " )
219
+ sb .WriteString (fmt .Sprintf (" Version: %s\n " , t .Message .version .String ()))
121
220
sb .WriteString (" Header:\n " )
122
221
sb .WriteString (fmt .Sprintf (" NumSignatures: %d\n " , t .Message .Header .NumSignatures ))
123
222
sb .WriteString (fmt .Sprintf (" NumReadOnly: %d\n " , t .Message .Header .NumReadOnly ))
124
223
sb .WriteString (fmt .Sprintf (" NumReadOnlySigned: %d\n " , t .Message .Header .NumReadonlySigned ))
125
- sb .WriteString (" Accounts:\n " )
224
+ sb .WriteString (" Static Accounts:\n " )
126
225
for i , a := range t .Message .Accounts {
127
226
sb .WriteString (fmt .Sprintf (" %d: %s\n " , i , base58 .Encode (a )))
128
227
}
@@ -133,7 +232,14 @@ func (t *Transaction) String() string {
133
232
sb .WriteString (fmt .Sprintf (" Accounts: %v\n " , t .Message .Instructions [i ].Accounts ))
134
233
sb .WriteString (fmt .Sprintf (" Data: %v\n " , t .Message .Instructions [i ].Data ))
135
234
}
136
-
235
+ if len (t .Message .AddressTableLookups ) > 0 {
236
+ sb .WriteString (" Address Table Lookups:\n " )
237
+ for i := range t .Message .AddressTableLookups {
238
+ sb .WriteString (fmt .Sprintf (" %s:\n " , base58 .Encode (t .Message .AddressTableLookups [i ].PublicKey )))
239
+ sb .WriteString (fmt .Sprintf (" Writable Indexes: %v\n " , t .Message .AddressTableLookups [i ].WritableIndexes ))
240
+ sb .WriteString (fmt .Sprintf (" Readonly Indexes: %v\n " , t .Message .AddressTableLookups [i ].ReadonlyIndexes ))
241
+ }
242
+ }
137
243
return sb .String ()
138
244
}
139
245
@@ -198,3 +304,13 @@ func indexOf(slice []ed25519.PublicKey, item ed25519.PublicKey) int {
198
304
199
305
return - 1
200
306
}
307
+
308
+ func (v MessageVersion ) String () string {
309
+ switch v {
310
+ case MessageVersionLegacy :
311
+ return "legacy"
312
+ case MessageVersion0 :
313
+ return "v0"
314
+ }
315
+ return "unknown"
316
+ }
0 commit comments