github.com/gagliardetto/solana-go@v1.11.0/message.go (about)

     1  // Copyright 2021 github.com/gagliardetto
     2  // This file has been modified by github.com/gagliardetto
     3  //
     4  // Copyright 2020 dfuse Platform Inc.
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //      http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  package solana
    19  
    20  import (
    21  	"encoding/base64"
    22  	"fmt"
    23  
    24  	bin "github.com/gagliardetto/binary"
    25  	"github.com/gagliardetto/treeout"
    26  
    27  	"github.com/gagliardetto/solana-go/text"
    28  )
    29  
    30  type MessageAddressTableLookupSlice []MessageAddressTableLookup
    31  
    32  // NumLookups returns the number of accounts from all the lookups.
    33  func (lookups MessageAddressTableLookupSlice) NumLookups() int {
    34  	count := 0
    35  	for _, lookup := range lookups {
    36  		count += len(lookup.ReadonlyIndexes)
    37  		count += len(lookup.WritableIndexes)
    38  	}
    39  	return count
    40  }
    41  
    42  // NumWritableLookups returns the number of writable accounts
    43  // across all the lookups (all the address tables).
    44  func (lookups MessageAddressTableLookupSlice) NumWritableLookups() int {
    45  	count := 0
    46  	for _, lookup := range lookups {
    47  		count += len(lookup.WritableIndexes)
    48  	}
    49  	return count
    50  }
    51  
    52  // GetTableIDs returns the list of all address table IDs.
    53  func (lookups MessageAddressTableLookupSlice) GetTableIDs() PublicKeySlice {
    54  	if lookups == nil {
    55  		return nil
    56  	}
    57  	ids := make(PublicKeySlice, 0)
    58  	for _, lookup := range lookups {
    59  		ids.UniqueAppend(lookup.AccountKey)
    60  	}
    61  	return ids
    62  }
    63  
    64  type MessageAddressTableLookup struct {
    65  	AccountKey      PublicKey       `json:"accountKey"` // The account key of the address table.
    66  	WritableIndexes Uint8SliceAsNum `json:"writableIndexes"`
    67  	ReadonlyIndexes Uint8SliceAsNum `json:"readonlyIndexes"`
    68  }
    69  
    70  // Uint8SliceAsNum is a slice of uint8s that can be marshaled as numbers instead of a byte slice.
    71  type Uint8SliceAsNum []uint8
    72  
    73  // MarshalJSON implements json.Marshaler.
    74  func (slice Uint8SliceAsNum) MarshalJSON() ([]byte, error) {
    75  	out := make([]uint16, len(slice))
    76  	for i, idx := range slice {
    77  		out[i] = uint16(idx)
    78  	}
    79  	return json.Marshal(out)
    80  }
    81  
    82  type MessageVersion int
    83  
    84  const (
    85  	MessageVersionLegacy MessageVersion = 0 // default
    86  	MessageVersionV0     MessageVersion = 1 // v0
    87  )
    88  
    89  type Message struct {
    90  	version MessageVersion
    91  	// List of base-58 encoded public keys used by the transaction,
    92  	// including by the instructions and for signatures.
    93  	// The first `message.header.numRequiredSignatures` public keys must sign the transaction.
    94  	AccountKeys PublicKeySlice `json:"accountKeys"` // static keys; static keys + dynamic keys if after resolution (i.e. call to `ResolveLookups()`)
    95  
    96  	// Details the account types and signatures required by the transaction.
    97  	Header MessageHeader `json:"header"`
    98  
    99  	// A base-58 encoded hash of a recent block in the ledger used to
   100  	// prevent transaction duplication and to give transactions lifetimes.
   101  	RecentBlockhash Hash `json:"recentBlockhash"`
   102  
   103  	// List of program instructions that will be executed in sequence
   104  	// and committed in one atomic transaction if all succeed.
   105  	Instructions []CompiledInstruction `json:"instructions"`
   106  
   107  	// List of address table lookups used to load additional accounts for this transaction.
   108  	AddressTableLookups MessageAddressTableLookupSlice `json:"addressTableLookups"`
   109  
   110  	// The actual tables that contain the list of account pubkeys.
   111  	// NOTE: you need to fetch these from the chain, and then call `SetAddressTables`
   112  	// before you use this transaction -- otherwise, you will get a panic.
   113  	addressTables map[PublicKey]PublicKeySlice
   114  
   115  	resolved bool // if true, the lookups have been resolved, and the `AccountKeys` slice contains all the accounts (static + dynamic).
   116  }
   117  
   118  // SetAddressTables sets the actual address tables used by this message.
   119  // Use `mx.GetAddressTableLookups().GetTableIDs()` to get the list of all address table IDs.
   120  // NOTE: you can call this once.
   121  func (mx *Message) SetAddressTables(tables map[PublicKey]PublicKeySlice) error {
   122  	if mx.addressTables != nil {
   123  		return fmt.Errorf("address tables already set")
   124  	}
   125  	mx.addressTables = tables
   126  	return nil
   127  }
   128  
   129  // GetAddressTables returns the actual address tables used by this message.
   130  // NOTE: you must have called `SetAddressTable` before being able to use this method.
   131  func (mx *Message) GetAddressTables() map[PublicKey]PublicKeySlice {
   132  	return mx.addressTables
   133  }
   134  
   135  var _ bin.EncoderDecoder = &Message{}
   136  
   137  // SetVersion sets the message version.
   138  // This method forces the message to be encoded in the specified version.
   139  // NOTE: if you set lookups, the version will default to V0.
   140  func (m *Message) SetVersion(version MessageVersion) *Message {
   141  	// check if the version is valid
   142  	switch version {
   143  	case MessageVersionV0, MessageVersionLegacy:
   144  	default:
   145  		panic(fmt.Errorf("invalid message version: %d", version))
   146  	}
   147  	m.version = version
   148  	return m
   149  }
   150  
   151  // GetVersion returns the message version.
   152  func (m *Message) GetVersion() MessageVersion {
   153  	return m.version
   154  }
   155  
   156  // SetAddressTableLookups (re)sets the lookups used by this message.
   157  func (mx *Message) SetAddressTableLookups(lookups []MessageAddressTableLookup) *Message {
   158  	mx.AddressTableLookups = lookups
   159  	mx.version = MessageVersionV0
   160  	return mx
   161  }
   162  
   163  // AddAddressTableLookup adds a new lookup to the message.
   164  func (mx *Message) AddAddressTableLookup(lookup MessageAddressTableLookup) *Message {
   165  	mx.AddressTableLookups = append(mx.AddressTableLookups, lookup)
   166  	mx.version = MessageVersionV0
   167  	return mx
   168  }
   169  
   170  // GetAddressTableLookups returns the lookups used by this message.
   171  func (mx *Message) GetAddressTableLookups() MessageAddressTableLookupSlice {
   172  	return mx.AddressTableLookups
   173  }
   174  
   175  func (mx Message) NumLookups() int {
   176  	if mx.AddressTableLookups == nil {
   177  		return 0
   178  	}
   179  	return mx.AddressTableLookups.NumLookups()
   180  }
   181  
   182  func (mx Message) NumWritableLookups() int {
   183  	if mx.AddressTableLookups == nil {
   184  		return 0
   185  	}
   186  	return mx.AddressTableLookups.NumWritableLookups()
   187  }
   188  
   189  func (mx Message) MarshalJSON() ([]byte, error) {
   190  	if mx.version == MessageVersionLegacy {
   191  		out := struct {
   192  			AccountKeys     []string              `json:"accountKeys"`
   193  			Header          MessageHeader         `json:"header"`
   194  			RecentBlockhash string                `json:"recentBlockhash"`
   195  			Instructions    []CompiledInstruction `json:"instructions"`
   196  		}{
   197  			AccountKeys:     make([]string, len(mx.AccountKeys)),
   198  			Header:          mx.Header,
   199  			RecentBlockhash: mx.RecentBlockhash.String(),
   200  			Instructions:    mx.Instructions,
   201  		}
   202  		for i, key := range mx.AccountKeys {
   203  			out.AccountKeys[i] = key.String()
   204  		}
   205  		return json.Marshal(out)
   206  	}
   207  	// Versioned message:
   208  	out := struct {
   209  		AccountKeys         []string                    `json:"accountKeys"`
   210  		Header              MessageHeader               `json:"header"`
   211  		RecentBlockhash     string                      `json:"recentBlockhash"`
   212  		Instructions        []CompiledInstruction       `json:"instructions"`
   213  		AddressTableLookups []MessageAddressTableLookup `json:"addressTableLookups"`
   214  	}{
   215  		AccountKeys:         make([]string, len(mx.AccountKeys)),
   216  		Header:              mx.Header,
   217  		RecentBlockhash:     mx.RecentBlockhash.String(),
   218  		Instructions:        mx.Instructions,
   219  		AddressTableLookups: mx.AddressTableLookups,
   220  	}
   221  	for i, key := range mx.AccountKeys {
   222  		out.AccountKeys[i] = key.String()
   223  	}
   224  	if out.AddressTableLookups == nil {
   225  		out.AddressTableLookups = make([]MessageAddressTableLookup, 0)
   226  	}
   227  	return json.Marshal(out)
   228  }
   229  
   230  func (mx *Message) EncodeToTree(txTree treeout.Branches) {
   231  	switch mx.version {
   232  	case MessageVersionV0:
   233  		txTree.Child("Version: v0")
   234  	case MessageVersionLegacy:
   235  		txTree.Child("Version: legacy")
   236  	default:
   237  		txTree.Child(text.Sf("Version (unknown): %v", mx.version))
   238  	}
   239  	txTree.Child(text.Sf("RecentBlockhash: %s", mx.RecentBlockhash))
   240  
   241  	txTree.Child(fmt.Sprintf("AccountKeys[len=%v]", mx.numStaticAccounts()+mx.AddressTableLookups.NumLookups())).ParentFunc(func(accountKeysBranch treeout.Branches) {
   242  		accountKeys, err := mx.AccountMetaList()
   243  		if err != nil {
   244  			accountKeysBranch.Child(text.RedBG(fmt.Sprintf("AccountMetaList: %s", err)))
   245  		} else {
   246  			for keyIndex, key := range accountKeys {
   247  				isFromTable := mx.IsVersioned() && keyIndex >= mx.numStaticAccounts()
   248  				if isFromTable {
   249  					accountKeysBranch.Child(text.Sf("%s (from Address Table Lookup)", text.ColorizeBG(key.PublicKey.String())))
   250  				} else {
   251  					accountKeysBranch.Child(text.ColorizeBG(key.PublicKey.String()))
   252  				}
   253  			}
   254  		}
   255  	})
   256  
   257  	if mx.IsVersioned() {
   258  		txTree.Child(fmt.Sprintf("AddressTableLookups[len=%v]", len(mx.AddressTableLookups))).ParentFunc(func(lookupsBranch treeout.Branches) {
   259  			for _, lookup := range mx.AddressTableLookups {
   260  				lookupsBranch.Child(text.Sf("%s", text.ColorizeBG(lookup.AccountKey.String()))).ParentFunc(func(lookupBranch treeout.Branches) {
   261  					lookupBranch.Child(text.Sf("WritableIndexes: %v", lookup.WritableIndexes))
   262  					lookupBranch.Child(text.Sf("ReadonlyIndexes: %v", lookup.ReadonlyIndexes))
   263  				})
   264  			}
   265  		})
   266  	}
   267  
   268  	txTree.Child("Header").ParentFunc(func(message treeout.Branches) {
   269  		mx.Header.EncodeToTree(message)
   270  	})
   271  }
   272  
   273  func (header *MessageHeader) EncodeToTree(mxBranch treeout.Branches) {
   274  	mxBranch.Child(text.Sf("NumRequiredSignatures: %v", header.NumRequiredSignatures))
   275  	mxBranch.Child(text.Sf("NumReadonlySignedAccounts: %v", header.NumReadonlySignedAccounts))
   276  	mxBranch.Child(text.Sf("NumReadonlyUnsignedAccounts: %v", header.NumReadonlyUnsignedAccounts))
   277  }
   278  
   279  func (mx *Message) MarshalBinary() ([]byte, error) {
   280  	switch mx.version {
   281  	case MessageVersionV0:
   282  		return mx.MarshalV0()
   283  	case MessageVersionLegacy:
   284  		return mx.MarshalLegacy()
   285  	default:
   286  		return nil, fmt.Errorf("invalid message version: %d", mx.version)
   287  	}
   288  }
   289  
   290  func (mx *Message) MarshalLegacy() ([]byte, error) {
   291  	buf := []byte{
   292  		mx.Header.NumRequiredSignatures,
   293  		mx.Header.NumReadonlySignedAccounts,
   294  		mx.Header.NumReadonlyUnsignedAccounts,
   295  	}
   296  
   297  	bin.EncodeCompactU16Length(&buf, len(mx.AccountKeys))
   298  	for _, key := range mx.AccountKeys {
   299  		buf = append(buf, key[:]...)
   300  	}
   301  
   302  	buf = append(buf, mx.RecentBlockhash[:]...)
   303  
   304  	bin.EncodeCompactU16Length(&buf, len(mx.Instructions))
   305  	for _, instruction := range mx.Instructions {
   306  		buf = append(buf, byte(instruction.ProgramIDIndex))
   307  		bin.EncodeCompactU16Length(&buf, len(instruction.Accounts))
   308  		for _, accountIdx := range instruction.Accounts {
   309  			buf = append(buf, byte(accountIdx))
   310  		}
   311  
   312  		bin.EncodeCompactU16Length(&buf, len(instruction.Data))
   313  		buf = append(buf, instruction.Data...)
   314  	}
   315  	return buf, nil
   316  }
   317  
   318  func (mx *Message) MarshalV0() ([]byte, error) {
   319  	buf := []byte{
   320  		mx.Header.NumRequiredSignatures,
   321  		mx.Header.NumReadonlySignedAccounts,
   322  		mx.Header.NumReadonlyUnsignedAccounts,
   323  	}
   324  	{
   325  		// Encode only the keys that are not in the address table lookups.
   326  		staticAccountKeys := mx.getStaticKeys()
   327  		bin.EncodeCompactU16Length(&buf, len(staticAccountKeys))
   328  		for _, key := range staticAccountKeys {
   329  			buf = append(buf, key[:]...)
   330  		}
   331  
   332  		buf = append(buf, mx.RecentBlockhash[:]...)
   333  
   334  		bin.EncodeCompactU16Length(&buf, len(mx.Instructions))
   335  		for _, instruction := range mx.Instructions {
   336  			buf = append(buf, byte(instruction.ProgramIDIndex))
   337  			bin.EncodeCompactU16Length(&buf, len(instruction.Accounts))
   338  			for _, accountIdx := range instruction.Accounts {
   339  				buf = append(buf, byte(accountIdx))
   340  			}
   341  
   342  			bin.EncodeCompactU16Length(&buf, len(instruction.Data))
   343  			buf = append(buf, instruction.Data...)
   344  		}
   345  	}
   346  	versionNum := byte(mx.version) // TODO: what number is this?
   347  	if versionNum > 127 {
   348  		return nil, fmt.Errorf("invalid message version: %d", mx.version)
   349  	}
   350  	buf = append([]byte{byte(versionNum + 127)}, buf...)
   351  
   352  	if mx.AddressTableLookups != nil && len(mx.AddressTableLookups) > 0 {
   353  		// wite length of address table lookups as u8
   354  		buf = append(buf, byte(len(mx.AddressTableLookups)))
   355  		for _, lookup := range mx.AddressTableLookups {
   356  			// write account pubkey
   357  			buf = append(buf, lookup.AccountKey[:]...)
   358  			// write writable indexes
   359  			bin.EncodeCompactU16Length(&buf, len(lookup.WritableIndexes))
   360  			buf = append(buf, lookup.WritableIndexes...)
   361  			// write readonly indexes
   362  			bin.EncodeCompactU16Length(&buf, len(lookup.ReadonlyIndexes))
   363  			buf = append(buf, lookup.ReadonlyIndexes...)
   364  		}
   365  	} else {
   366  		buf = append(buf, 0)
   367  	}
   368  	return buf, nil
   369  }
   370  
   371  func (mx Message) MarshalWithEncoder(encoder *bin.Encoder) error {
   372  	out, err := mx.MarshalBinary()
   373  	if err != nil {
   374  		return err
   375  	}
   376  	return encoder.WriteBytes(out, false)
   377  }
   378  
   379  func (mx Message) ToBase64() string {
   380  	out, _ := mx.MarshalBinary()
   381  	return base64.StdEncoding.EncodeToString(out)
   382  }
   383  
   384  func (mx *Message) UnmarshalWithDecoder(decoder *bin.Decoder) (err error) {
   385  	// peek first byte to determine if this is a legacy or v0 message
   386  	versionNum, err := decoder.Peek(1)
   387  	if err != nil {
   388  		return err
   389  	}
   390  	// TODO: is this the right way to determine if this is a legacy or v0 message?
   391  	if versionNum[0] < 127 {
   392  		mx.version = MessageVersionLegacy
   393  	} else {
   394  		mx.version = MessageVersionV0
   395  	}
   396  	switch mx.version {
   397  	case MessageVersionV0:
   398  		return mx.UnmarshalV0(decoder)
   399  	case MessageVersionLegacy:
   400  		return mx.UnmarshalLegacy(decoder)
   401  	default:
   402  		return fmt.Errorf("invalid message version: %d", mx.version)
   403  	}
   404  }
   405  
   406  func (mx *Message) UnmarshalBase64(b64 string) error {
   407  	b, err := base64.StdEncoding.DecodeString(b64)
   408  	if err != nil {
   409  		return err
   410  	}
   411  	return mx.UnmarshalWithDecoder(bin.NewBinDecoder(b))
   412  }
   413  
   414  // GetAddressTableLookupAccounts associates the lookups with the accounts
   415  // in the actual address tables, and returns the accounts.
   416  // NOTE: you need to call `SetAddressTables` before calling this method,
   417  // so that the lookups can be associated with the accounts in the address tables.
   418  func (mx Message) GetAddressTableLookupAccounts() (PublicKeySlice, error) {
   419  	err := mx.checkPreconditions()
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  	var writable PublicKeySlice
   424  	var readonly PublicKeySlice
   425  
   426  	for _, lookup := range mx.AddressTableLookups {
   427  		table, ok := mx.addressTables[lookup.AccountKey]
   428  		if !ok {
   429  			return writable, fmt.Errorf("address table lookup not found for account: %s", lookup.AccountKey)
   430  		}
   431  		for _, idx := range lookup.WritableIndexes {
   432  			if int(idx) >= len(table) {
   433  				return writable, fmt.Errorf("address table lookup index out of range: %d", idx)
   434  			}
   435  			writable = append(writable, table[idx])
   436  		}
   437  		for _, idx := range lookup.ReadonlyIndexes {
   438  			if int(idx) >= len(table) {
   439  				return writable, fmt.Errorf("address table lookup index out of range: %d", idx)
   440  			}
   441  			readonly = append(readonly, table[idx])
   442  		}
   443  	}
   444  
   445  	return append(writable, readonly...), nil
   446  }
   447  
   448  // ResolveLookups resolves the address table lookups,
   449  // and appends the resolved accounts to the `message.AccountKeys` field.
   450  // NOTE: you need to call `SetAddressTables` before calling this method.
   451  func (mx *Message) ResolveLookups() (err error) {
   452  	if mx.resolved {
   453  		return nil
   454  	}
   455  	// add accounts from the address table lookups
   456  	atlAccounts, err := mx.GetAddressTableLookupAccounts()
   457  	if err != nil {
   458  		return err
   459  	}
   460  	mx.AccountKeys = append(mx.AccountKeys, atlAccounts...)
   461  	mx.resolved = true
   462  
   463  	return nil
   464  }
   465  
   466  func (mx Message) IsResolved() bool {
   467  	return mx.resolved
   468  }
   469  
   470  // GetAllKeys returns ALL the message's account keys (including the keys from resolved address lookup tables).
   471  func (mx Message) GetAllKeys() (keys PublicKeySlice, err error) {
   472  	if mx.resolved {
   473  		// If the message has been resolved, then the account keys have already
   474  		// been appended to the `AccountKeys` field of the message.
   475  		return mx.AccountKeys, nil
   476  	}
   477  	// If not resolved, then we need to resolve the lookups first...
   478  	atlAccounts, err := mx.GetAddressTableLookupAccounts()
   479  	if err != nil {
   480  		return keys, err
   481  	}
   482  	// ...and return the account keys with the lookups appended:
   483  	return append(mx.AccountKeys, atlAccounts...), nil
   484  }
   485  
   486  func (mx Message) getStaticKeys() (keys PublicKeySlice) {
   487  	if mx.resolved {
   488  		// If the message has been resolved, then the account keys have already
   489  		// been appended to the `AccountKeys` field of the message.
   490  		return mx.AccountKeys[:mx.numStaticAccounts()]
   491  	}
   492  	return mx.AccountKeys
   493  }
   494  
   495  func (mx *Message) UnmarshalV0(decoder *bin.Decoder) (err error) {
   496  	version, err := decoder.ReadByte()
   497  	if err != nil {
   498  		return fmt.Errorf("failed to read message version: %w", err)
   499  	}
   500  	// TODO: check version
   501  	mx.version = MessageVersion(version - 127)
   502  
   503  	// The middle of the message is the same as the legacy message:
   504  	err = mx.UnmarshalLegacy(decoder)
   505  	if err != nil {
   506  		return err
   507  	}
   508  
   509  	// Read address table lookups length:
   510  	addressTableLookupsLen, err := decoder.ReadByte()
   511  	if err != nil {
   512  		return fmt.Errorf("failed to read address table lookups length: %w", err)
   513  	}
   514  	if addressTableLookupsLen > 0 {
   515  		mx.AddressTableLookups = make([]MessageAddressTableLookup, addressTableLookupsLen)
   516  		for i := 0; i < int(addressTableLookupsLen); i++ {
   517  			// read account pubkey
   518  			_, err = decoder.Read(mx.AddressTableLookups[i].AccountKey[:])
   519  			if err != nil {
   520  				return fmt.Errorf("failed to read account pubkey: %w", err)
   521  			}
   522  
   523  			// read writable indexes
   524  			writableIndexesLen, err := decoder.ReadCompactU16()
   525  			if err != nil {
   526  				return fmt.Errorf("failed to read writable indexes length: %w", err)
   527  			}
   528  			if writableIndexesLen > decoder.Remaining() {
   529  				return fmt.Errorf("writable indexes length is too large: %d", writableIndexesLen)
   530  			}
   531  			mx.AddressTableLookups[i].WritableIndexes = make([]byte, writableIndexesLen)
   532  			_, err = decoder.Read(mx.AddressTableLookups[i].WritableIndexes)
   533  			if err != nil {
   534  				return fmt.Errorf("failed to read writable indexes: %w", err)
   535  			}
   536  
   537  			// read readonly indexes
   538  			readonlyIndexesLen, err := decoder.ReadCompactU16()
   539  			if err != nil {
   540  				return fmt.Errorf("failed to read readonly indexes length: %w", err)
   541  			}
   542  			if readonlyIndexesLen > decoder.Remaining() {
   543  				return fmt.Errorf("readonly indexes length is too large: %d", readonlyIndexesLen)
   544  			}
   545  			mx.AddressTableLookups[i].ReadonlyIndexes = make([]byte, readonlyIndexesLen)
   546  			_, err = decoder.Read(mx.AddressTableLookups[i].ReadonlyIndexes)
   547  			if err != nil {
   548  				return fmt.Errorf("failed to read readonly indexes: %w", err)
   549  			}
   550  		}
   551  	}
   552  	return nil
   553  }
   554  
   555  func (mx *Message) UnmarshalLegacy(decoder *bin.Decoder) (err error) {
   556  	{
   557  		mx.Header.NumRequiredSignatures, err = decoder.ReadUint8()
   558  		if err != nil {
   559  			return fmt.Errorf("unable to decode mx.Header.NumRequiredSignatures: %w", err)
   560  		}
   561  		mx.Header.NumReadonlySignedAccounts, err = decoder.ReadUint8()
   562  		if err != nil {
   563  			return fmt.Errorf("unable to decode mx.Header.NumReadonlySignedAccounts: %w", err)
   564  		}
   565  		mx.Header.NumReadonlyUnsignedAccounts, err = decoder.ReadUint8()
   566  		if err != nil {
   567  			return fmt.Errorf("unable to decode mx.Header.NumReadonlyUnsignedAccounts: %w", err)
   568  		}
   569  	}
   570  	{
   571  		numAccountKeys, err := decoder.ReadCompactU16()
   572  		if err != nil {
   573  			return fmt.Errorf("unable to decode numAccountKeys: %w", err)
   574  		}
   575  		if numAccountKeys > decoder.Remaining()/32 {
   576  			return fmt.Errorf("numAccountKeys %d is too large for remaining bytes %d", numAccountKeys, decoder.Remaining())
   577  		}
   578  		mx.AccountKeys = make(PublicKeySlice, numAccountKeys)
   579  		for i := 0; i < numAccountKeys; i++ {
   580  			_, err := decoder.Read(mx.AccountKeys[i][:])
   581  			if err != nil {
   582  				return fmt.Errorf("unable to decode mx.AccountKeys[%d]: %w", i, err)
   583  			}
   584  		}
   585  	}
   586  	{
   587  		_, err := decoder.Read(mx.RecentBlockhash[:])
   588  		if err != nil {
   589  			return fmt.Errorf("unable to decode mx.RecentBlockhash: %w", err)
   590  		}
   591  	}
   592  	{
   593  		numInstructions, err := decoder.ReadCompactU16()
   594  		if err != nil {
   595  			return fmt.Errorf("unable to decode numInstructions: %w", err)
   596  		}
   597  		if numInstructions > decoder.Remaining() {
   598  			return fmt.Errorf("numInstructions %d is greater than remaining bytes %d", numInstructions, decoder.Remaining())
   599  		}
   600  		mx.Instructions = make([]CompiledInstruction, numInstructions)
   601  		for instructionIndex := 0; instructionIndex < numInstructions; instructionIndex++ {
   602  			programIDIndex, err := decoder.ReadUint8()
   603  			if err != nil {
   604  				return fmt.Errorf("unable to decode mx.Instructions[%d].ProgramIDIndex: %w", instructionIndex, err)
   605  			}
   606  			mx.Instructions[instructionIndex].ProgramIDIndex = uint16(programIDIndex)
   607  
   608  			{
   609  				numAccounts, err := decoder.ReadCompactU16()
   610  				if err != nil {
   611  					return fmt.Errorf("unable to decode numAccounts for ix[%d]: %w", instructionIndex, err)
   612  				}
   613  				if numAccounts > decoder.Remaining() {
   614  					return fmt.Errorf("ix[%v]: numAccounts %d is greater than remaining bytes %d", instructionIndex, numAccounts, decoder.Remaining())
   615  				}
   616  				mx.Instructions[instructionIndex].Accounts = make([]uint16, numAccounts)
   617  				for i := 0; i < numAccounts; i++ {
   618  					accountIndex, err := decoder.ReadUint8()
   619  					if err != nil {
   620  						return fmt.Errorf("unable to decode accountIndex for ix[%d].Accounts[%d]: %w", instructionIndex, i, err)
   621  					}
   622  					mx.Instructions[instructionIndex].Accounts[i] = uint16(accountIndex)
   623  				}
   624  			}
   625  			{
   626  				dataLen, err := decoder.ReadCompactU16()
   627  				if err != nil {
   628  					return fmt.Errorf("unable to decode dataLen for ix[%d]: %w", instructionIndex, err)
   629  				}
   630  				if dataLen > decoder.Remaining() {
   631  					return fmt.Errorf("ix[%v]: dataLen %d is greater than remaining bytes %d", instructionIndex, dataLen, decoder.Remaining())
   632  				}
   633  				dataBytes, err := decoder.ReadBytes(dataLen)
   634  				if err != nil {
   635  					return fmt.Errorf("unable to decode dataBytes for ix[%d]: %w", instructionIndex, err)
   636  				}
   637  				mx.Instructions[instructionIndex].Data = (Base58)(dataBytes)
   638  			}
   639  		}
   640  	}
   641  
   642  	return nil
   643  }
   644  
   645  func (m Message) checkPreconditions() error {
   646  	// if this is versioned,
   647  	// and there are > 0 lookups,
   648  	// but the address table is empty,
   649  	// then we can't build the account meta list:
   650  	if m.IsVersioned() && m.AddressTableLookups.NumLookups() > 0 && (m.addressTables == nil || len(m.addressTables) == 0) {
   651  		return fmt.Errorf("cannot build account meta list without address tables")
   652  	}
   653  
   654  	return nil
   655  }
   656  
   657  func (m Message) AccountMetaList() (AccountMetaSlice, error) {
   658  	err := m.checkPreconditions()
   659  	if err != nil {
   660  		return nil, err
   661  	}
   662  	accountKeys, err := m.GetAllKeys()
   663  	if err != nil {
   664  		return nil, err
   665  	}
   666  	out := make(AccountMetaSlice, len(accountKeys))
   667  
   668  	for i, a := range accountKeys {
   669  		isWritable, err := m.IsWritable(a)
   670  		if err != nil {
   671  			return nil, err
   672  		}
   673  
   674  		out[i] = &AccountMeta{
   675  			PublicKey:  a,
   676  			IsSigner:   m.IsSigner(a),
   677  			IsWritable: isWritable,
   678  		}
   679  	}
   680  
   681  	return out, nil
   682  }
   683  
   684  func (m Message) IsVersioned() bool {
   685  	return m.version != MessageVersionLegacy
   686  }
   687  
   688  // Signers returns the pubkeys of all accounts that are signers.
   689  func (m Message) Signers() PublicKeySlice {
   690  	// signers always in AccountKeys
   691  	out := make(PublicKeySlice, 0, len(m.AccountKeys))
   692  	for _, a := range m.AccountKeys {
   693  		if m.IsSigner(a) {
   694  			out = append(out, a)
   695  		}
   696  	}
   697  
   698  	return out
   699  }
   700  
   701  // Writable returns the pubkeys of all accounts that are writable.
   702  func (m Message) Writable() (out PublicKeySlice, err error) {
   703  	err = m.checkPreconditions()
   704  	if err != nil {
   705  		return nil, err
   706  	}
   707  	accountKeys, err := m.GetAllKeys()
   708  	if err != nil {
   709  		return nil, err
   710  	}
   711  
   712  	for _, a := range accountKeys {
   713  		isWritable, err := m.IsWritable(a)
   714  		if err != nil {
   715  			return nil, err
   716  		}
   717  
   718  		if isWritable {
   719  			out = append(out, a)
   720  		}
   721  	}
   722  
   723  	return out, nil
   724  }
   725  
   726  // ResolveProgramIDIndex resolves the program ID index to a program ID.
   727  // DEPRECATED: use `Program(index)` instead.
   728  func (m Message) ResolveProgramIDIndex(programIDIndex uint16) (PublicKey, error) {
   729  	return m.Program(programIDIndex)
   730  }
   731  
   732  // Program returns the program key at the given index.
   733  func (m Message) Program(programIDIndex uint16) (PublicKey, error) {
   734  	// programIDIndex always in AccountKeys
   735  	if int(programIDIndex) < len(m.AccountKeys) {
   736  		return m.AccountKeys[programIDIndex], nil
   737  	}
   738  	return PublicKey{}, fmt.Errorf("programID index not found %d", programIDIndex)
   739  }
   740  
   741  // Account returns the account at the given index.
   742  func (m Message) Account(index uint16) (PublicKey, error) {
   743  	if int(index) < len(m.AccountKeys) {
   744  		return m.AccountKeys[index], nil
   745  	}
   746  	allKeys, err := m.GetAllKeys()
   747  	if err != nil {
   748  		return PublicKey{}, err
   749  	}
   750  	if int(index) < len(allKeys) {
   751  		return allKeys[index], nil
   752  	}
   753  	return PublicKey{}, fmt.Errorf("account index not found %d", index)
   754  }
   755  
   756  // GetAccountIndex returns the index of the given account (first occurrence of the account).
   757  func (m Message) GetAccountIndex(account PublicKey) (uint16, error) {
   758  	err := m.checkPreconditions()
   759  	if err != nil {
   760  		return 0, err
   761  	}
   762  	accountKeys, err := m.GetAllKeys()
   763  	if err != nil {
   764  		return 0, err
   765  	}
   766  
   767  	for idx, a := range accountKeys {
   768  		if a.Equals(account) {
   769  			return uint16(idx), nil
   770  		}
   771  	}
   772  
   773  	return 0, fmt.Errorf("account not found: %s", account)
   774  }
   775  
   776  func (m Message) HasAccount(account PublicKey) (bool, error) {
   777  	err := m.checkPreconditions()
   778  	if err != nil {
   779  		return false, err
   780  	}
   781  	accountKeys, err := m.GetAllKeys()
   782  	if err != nil {
   783  		return false, err
   784  	}
   785  
   786  	for _, a := range accountKeys {
   787  		if a.Equals(account) {
   788  			return true, nil
   789  		}
   790  	}
   791  
   792  	return false, nil
   793  }
   794  
   795  func (m Message) IsSigner(account PublicKey) bool {
   796  	// signers always in AccountKeys
   797  	for idx, acc := range m.AccountKeys {
   798  		if acc.Equals(account) {
   799  			return idx < int(m.Header.NumRequiredSignatures)
   800  		}
   801  	}
   802  	return false
   803  }
   804  
   805  // numStaticAccounts returns the number of accounts that are always present in the
   806  // account keys list (i.e. all the accounts that are NOT in the lookup table).
   807  func (m Message) numStaticAccounts() int {
   808  	if !m.resolved {
   809  		return len(m.AccountKeys)
   810  	}
   811  	return len(m.AccountKeys) - m.NumLookups()
   812  }
   813  
   814  func (m Message) isWritableInLookups(idx int) bool {
   815  	if idx < m.numStaticAccounts() {
   816  		return false
   817  	}
   818  	return idx-m.numStaticAccounts() < m.AddressTableLookups.NumWritableLookups()
   819  }
   820  
   821  func (m Message) IsWritable(account PublicKey) (bool, error) {
   822  	err := m.checkPreconditions()
   823  	if err != nil {
   824  		return false, err
   825  	}
   826  	accountKeys, err := m.GetAllKeys()
   827  	if err != nil {
   828  		return false, err
   829  	}
   830  
   831  	index := 0
   832  	found := false
   833  	for idx, acc := range accountKeys {
   834  		if acc.Equals(account) {
   835  			found = true
   836  			index = idx
   837  		}
   838  	}
   839  	if !found {
   840  		return false, err
   841  	}
   842  	h := m.Header
   843  
   844  	if index >= m.numStaticAccounts() {
   845  		return m.isWritableInLookups(index), nil
   846  	} else if index >= int(h.NumRequiredSignatures) {
   847  		// unsignedAccountIndex < numWritableUnsignedAccounts
   848  		return index-int(h.NumRequiredSignatures) < (m.numStaticAccounts()-int(h.NumRequiredSignatures))-int(h.NumReadonlyUnsignedAccounts), nil
   849  	}
   850  	return index < int(h.NumRequiredSignatures-h.NumReadonlySignedAccounts), nil
   851  }
   852  
   853  func (m Message) signerKeys() PublicKeySlice {
   854  	return m.AccountKeys[0:m.Header.NumRequiredSignatures]
   855  }
   856  
   857  type MessageHeader struct {
   858  	// The total number of signatures required to make the transaction valid.
   859  	// The signatures must match the first `numRequiredSignatures` of `message.account_keys`.
   860  	NumRequiredSignatures uint8 `json:"numRequiredSignatures"`
   861  
   862  	// The last numReadonlySignedAccounts of the signed keys are read-only accounts.
   863  	// Programs may process multiple transactions that load read-only accounts within
   864  	// a single PoH entry, but are not permitted to credit or debit lamports or modify
   865  	// account data.
   866  	// Transactions targeting the same read-write account are evaluated sequentially.
   867  	NumReadonlySignedAccounts uint8 `json:"numReadonlySignedAccounts"`
   868  
   869  	// The last `numReadonlyUnsignedAccounts` of the unsigned keys are read-only accounts.
   870  	NumReadonlyUnsignedAccounts uint8 `json:"numReadonlyUnsignedAccounts"`
   871  }