@@ -20,17 +20,32 @@ const (
2020type Signature [ed25519 .SignatureSize ]byte
2121type Blockhash [sha256 .Size ]byte
2222
23+ type MessageVersion uint8
24+
25+ const (
26+ MessageVersionLegacy MessageVersion = iota
27+ MessageVersion0
28+ )
29+
2330type Header struct {
2431 NumSignatures byte
2532 NumReadonlySigned byte
2633 NumReadOnly byte
2734}
2835
36+ type MessageAddressTableLookup struct {
37+ PublicKey ed25519.PublicKey
38+ WritableIndexes []byte
39+ ReadonlyIndexes []byte
40+ }
41+
2942type 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
3449}
3550
3651type Transaction struct {
@@ -39,6 +54,15 @@ type Transaction struct {
3954}
4055
4156func 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 {
4266 accounts := []AccountMeta {
4367 {
4468 PublicKey : payer ,
@@ -65,8 +89,45 @@ func NewTransaction(payer ed25519.PublicKey, instructions ...Instruction) Transa
6589 accounts = filterUnique (accounts )
6690 sort .Sort (SortableAccountMeta (accounts ))
6791
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+
68100 var m Message
69101 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
70131 m .Accounts = append (m .Accounts , account .PublicKey )
71132
72133 if account .IsSigner {
@@ -80,21 +141,60 @@ func NewTransaction(payer ed25519.PublicKey, instructions ...Instruction) Transa
80141 }
81142 }
82143
144+ fmt .Println (readonlyAddressTableIndexes )
145+
146+ // Consolidate static and dynamically loaded accounts into an ordered list,
147+ // which is used for index references encoded in the message
148+ dynamicWritableAccounts := make ([]ed25519.PublicKey , 0 )
149+ dynamicReadonlyAccount := make ([]ed25519.PublicKey , 0 )
150+ for i , writableAddressTableIndexes := range writableAddressTableIndexes {
151+ for _ , index := range writableAddressTableIndexes {
152+ writableAccount := sortedAddressLookupTables [i ].Addresses [index ]
153+ dynamicWritableAccounts = append (dynamicWritableAccounts , writableAccount )
154+ }
155+ }
156+ for i , readonlyAddressTableIndexes := range readonlyAddressTableIndexes {
157+ for _ , index := range readonlyAddressTableIndexes {
158+ readonlyAccount := sortedAddressLookupTables [i ].Addresses [index ]
159+ dynamicReadonlyAccount = append (dynamicReadonlyAccount , readonlyAccount )
160+ }
161+ }
162+ var allAccounts []ed25519.PublicKey
163+ allAccounts = append (allAccounts , m .Accounts ... )
164+ allAccounts = append (allAccounts , dynamicWritableAccounts ... )
165+ allAccounts = append (allAccounts , dynamicReadonlyAccount ... )
166+
83167 // Generate the compiled instruction, which uses indices instead
84168 // of raw account keys.
85169 for _ , i := range instructions {
86170 c := CompiledInstruction {
87- ProgramIndex : byte (indexOf (m . Accounts , i .Program )),
171+ ProgramIndex : byte (indexOf (allAccounts , i .Program )),
88172 Data : i .Data ,
89173 }
90174
91175 for _ , a := range i .Accounts {
92- c .Accounts = append (c .Accounts , byte (indexOf (m . Accounts , a .PublicKey )))
176+ c .Accounts = append (c .Accounts , byte (indexOf (allAccounts , a .PublicKey )))
93177 }
94178
95179 m .Instructions = append (m .Instructions , c )
96180 }
97181
182+ // Generate the compiled message address table lookups
183+ for i , addressLookupTable := range sortedAddressLookupTables {
184+ if len (writableAddressTableIndexes [i ]) == 0 && len (readonlyAddressTableIndexes [i ]) == 0 {
185+ continue
186+ }
187+
188+ m .AddressTableLookups = append (m .AddressTableLookups , MessageAddressTableLookup {
189+ PublicKey : addressLookupTable .PublicKey ,
190+ WritableIndexes : writableAddressTableIndexes [i ],
191+ ReadonlyIndexes : readonlyAddressTableIndexes [i ],
192+ })
193+ }
194+ if len (m .AddressTableLookups ) > 0 {
195+ m .version = MessageVersion0
196+ }
197+
98198 for i := range m .Accounts {
99199 if len (m .Accounts [i ]) == 0 {
100200 m .Accounts [i ] = make ([]byte , ed25519 .PublicKeySize )
@@ -118,11 +218,12 @@ func (t *Transaction) String() string {
118218 sb .WriteString (fmt .Sprintf (" %d: %s\n " , i , base58 .Encode (s [:])))
119219 }
120220 sb .WriteString ("Message:\n " )
221+ sb .WriteString (fmt .Sprintf (" Version: %s\n " , t .Message .version .String ()))
121222 sb .WriteString (" Header:\n " )
122223 sb .WriteString (fmt .Sprintf (" NumSignatures: %d\n " , t .Message .Header .NumSignatures ))
123224 sb .WriteString (fmt .Sprintf (" NumReadOnly: %d\n " , t .Message .Header .NumReadOnly ))
124225 sb .WriteString (fmt .Sprintf (" NumReadOnlySigned: %d\n " , t .Message .Header .NumReadonlySigned ))
125- sb .WriteString (" Accounts:\n " )
226+ sb .WriteString (" Static Accounts:\n " )
126227 for i , a := range t .Message .Accounts {
127228 sb .WriteString (fmt .Sprintf (" %d: %s\n " , i , base58 .Encode (a )))
128229 }
@@ -133,7 +234,14 @@ func (t *Transaction) String() string {
133234 sb .WriteString (fmt .Sprintf (" Accounts: %v\n " , t .Message .Instructions [i ].Accounts ))
134235 sb .WriteString (fmt .Sprintf (" Data: %v\n " , t .Message .Instructions [i ].Data ))
135236 }
136-
237+ if len (t .Message .AddressTableLookups ) > 0 {
238+ sb .WriteString (" Address Table Lookups:\n " )
239+ for i := range t .Message .AddressTableLookups {
240+ sb .WriteString (fmt .Sprintf (" %s:\n " , base58 .Encode (t .Message .AddressTableLookups [i ].PublicKey )))
241+ sb .WriteString (fmt .Sprintf (" Writable Indexes: %v\n " , t .Message .AddressTableLookups [i ].WritableIndexes ))
242+ sb .WriteString (fmt .Sprintf (" Readonly Indexes: %v\n " , t .Message .AddressTableLookups [i ].ReadonlyIndexes ))
243+ }
244+ }
137245 return sb .String ()
138246}
139247
@@ -198,3 +306,13 @@ func indexOf(slice []ed25519.PublicKey, item ed25519.PublicKey) int {
198306
199307 return - 1
200308}
309+
310+ func (v MessageVersion ) String () string {
311+ switch v {
312+ case MessageVersionLegacy :
313+ return "legacy"
314+ case MessageVersion0 :
315+ return "v0"
316+ }
317+ return "unknown"
318+ }
0 commit comments