github.com/gagliardetto/solana-go@v1.11.0/transaction.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  	"bytes"
    22  	"encoding/base64"
    23  	"fmt"
    24  	"sort"
    25  
    26  	"github.com/davecgh/go-spew/spew"
    27  	bin "github.com/gagliardetto/binary"
    28  	"github.com/gagliardetto/treeout"
    29  	"github.com/mr-tron/base58"
    30  	"go.uber.org/zap"
    31  
    32  	"github.com/gagliardetto/solana-go/text"
    33  )
    34  
    35  type Transaction struct {
    36  	// A list of base-58 encoded signatures applied to the transaction.
    37  	// The list is always of length `message.header.numRequiredSignatures` and not empty.
    38  	// The signature at index `i` corresponds to the public key at index
    39  	// `i` in `message.account_keys`. The first one is used as the transaction id.
    40  	Signatures []Signature `json:"signatures"`
    41  
    42  	// Defines the content of the transaction.
    43  	Message Message `json:"message"`
    44  }
    45  
    46  // UnmarshalBase64 decodes a base64 encoded transaction.
    47  func (tx *Transaction) UnmarshalBase64(b64 string) error {
    48  	b, err := base64.StdEncoding.DecodeString(b64)
    49  	if err != nil {
    50  		return err
    51  	}
    52  	return tx.UnmarshalWithDecoder(bin.NewBinDecoder(b))
    53  }
    54  
    55  var _ bin.EncoderDecoder = &Transaction{}
    56  
    57  func (t *Transaction) HasAccount(account PublicKey) (bool, error) {
    58  	return t.Message.HasAccount(account)
    59  }
    60  
    61  func (t *Transaction) IsSigner(account PublicKey) bool {
    62  	return t.Message.IsSigner(account)
    63  }
    64  
    65  func (t *Transaction) IsWritable(account PublicKey) (bool, error) {
    66  	return t.Message.IsWritable(account)
    67  }
    68  
    69  func (t *Transaction) AccountMetaList() ([]*AccountMeta, error) {
    70  	return t.Message.AccountMetaList()
    71  }
    72  
    73  func (t *Transaction) ResolveProgramIDIndex(programIDIndex uint16) (PublicKey, error) {
    74  	return t.Message.ResolveProgramIDIndex(programIDIndex)
    75  }
    76  
    77  func (t *Transaction) GetAccountIndex(account PublicKey) (uint16, error) {
    78  	return t.Message.GetAccountIndex(account)
    79  }
    80  
    81  // TransactionFromDecoder decodes a transaction from a decoder.
    82  func TransactionFromDecoder(decoder *bin.Decoder) (*Transaction, error) {
    83  	var out *Transaction
    84  	err := decoder.Decode(&out)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	return out, nil
    89  }
    90  
    91  func TransactionFromBytes(data []byte) (*Transaction, error) {
    92  	decoder := bin.NewBinDecoder(data)
    93  	return TransactionFromDecoder(decoder)
    94  }
    95  
    96  func TransactionFromBase64(b64 string) (*Transaction, error) {
    97  	data, err := base64.StdEncoding.DecodeString(b64)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	return TransactionFromBytes(data)
   102  }
   103  
   104  func TransactionFromBase58(b58 string) (*Transaction, error) {
   105  	data, err := base58.Decode(b58)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	return TransactionFromBytes(data)
   110  }
   111  
   112  // MustTransactionFromDecoder decodes a transaction from a decoder.
   113  // Panics on error.
   114  func MustTransactionFromDecoder(decoder *bin.Decoder) *Transaction {
   115  	out, err := TransactionFromDecoder(decoder)
   116  	if err != nil {
   117  		panic(err)
   118  	}
   119  	return out
   120  }
   121  
   122  type CompiledInstruction struct {
   123  	// Index into the message.accountKeys array indicating the program account that executes this instruction.
   124  	// NOTE: it is actually a uint8, but using a uint16 because uint8 is treated as a byte everywhere,
   125  	// and that can be an issue.
   126  	ProgramIDIndex uint16 `json:"programIdIndex"`
   127  
   128  	// List of ordered indices into the message.accountKeys array indicating which accounts to pass to the program.
   129  	// NOTE: it is actually a []uint8, but using a uint16 because []uint8 is treated as a []byte everywhere,
   130  	// and that can be an issue.
   131  	Accounts []uint16 `json:"accounts"`
   132  
   133  	// The program input data encoded in a base-58 string.
   134  	Data Base58 `json:"data"`
   135  }
   136  
   137  func (ci *CompiledInstruction) ResolveInstructionAccounts(message *Message) ([]*AccountMeta, error) {
   138  	out := make([]*AccountMeta, len(ci.Accounts))
   139  	metas, err := message.AccountMetaList()
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	for i, acct := range ci.Accounts {
   144  		out[i] = metas[acct]
   145  	}
   146  
   147  	return out, nil
   148  }
   149  
   150  type Instruction interface {
   151  	ProgramID() PublicKey     // the programID the instruction acts on
   152  	Accounts() []*AccountMeta // returns the list of accounts the instructions requires
   153  	Data() ([]byte, error)    // the binary encoded instructions
   154  }
   155  
   156  type TransactionOption interface {
   157  	apply(opts *transactionOptions)
   158  }
   159  
   160  type transactionOptions struct {
   161  	payer         PublicKey
   162  	addressTables map[PublicKey]PublicKeySlice // [tablePubkey]addresses
   163  }
   164  
   165  type transactionOptionFunc func(opts *transactionOptions)
   166  
   167  func (f transactionOptionFunc) apply(opts *transactionOptions) {
   168  	f(opts)
   169  }
   170  
   171  func TransactionPayer(payer PublicKey) TransactionOption {
   172  	return transactionOptionFunc(func(opts *transactionOptions) { opts.payer = payer })
   173  }
   174  
   175  func TransactionAddressTables(tables map[PublicKey]PublicKeySlice) TransactionOption {
   176  	return transactionOptionFunc(func(opts *transactionOptions) { opts.addressTables = tables })
   177  }
   178  
   179  var debugNewTransaction = false
   180  
   181  type TransactionBuilder struct {
   182  	instructions    []Instruction
   183  	recentBlockHash Hash
   184  	opts            []TransactionOption
   185  }
   186  
   187  // NewTransactionBuilder creates a new instruction builder.
   188  func NewTransactionBuilder() *TransactionBuilder {
   189  	return &TransactionBuilder{}
   190  }
   191  
   192  // AddInstruction adds the provided instruction to the builder.
   193  func (builder *TransactionBuilder) AddInstruction(instruction Instruction) *TransactionBuilder {
   194  	builder.instructions = append(builder.instructions, instruction)
   195  	return builder
   196  }
   197  
   198  // SetRecentBlockHash sets the recent blockhash for the instruction builder.
   199  func (builder *TransactionBuilder) SetRecentBlockHash(recentBlockHash Hash) *TransactionBuilder {
   200  	builder.recentBlockHash = recentBlockHash
   201  	return builder
   202  }
   203  
   204  // WithOpt adds a TransactionOption.
   205  func (builder *TransactionBuilder) WithOpt(opt TransactionOption) *TransactionBuilder {
   206  	builder.opts = append(builder.opts, opt)
   207  	return builder
   208  }
   209  
   210  // Set transaction fee payer.
   211  // If not set, defaults to first signer account of the first instruction.
   212  func (builder *TransactionBuilder) SetFeePayer(feePayer PublicKey) *TransactionBuilder {
   213  	builder.opts = append(builder.opts, TransactionPayer(feePayer))
   214  	return builder
   215  }
   216  
   217  // Build builds and returns a *Transaction.
   218  func (builder *TransactionBuilder) Build() (*Transaction, error) {
   219  	return NewTransaction(
   220  		builder.instructions,
   221  		builder.recentBlockHash,
   222  		builder.opts...,
   223  	)
   224  }
   225  
   226  type addressTablePubkeyWithIndex struct {
   227  	addressTable PublicKey
   228  	index        uint8
   229  }
   230  
   231  func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...TransactionOption) (*Transaction, error) {
   232  	if len(instructions) == 0 {
   233  		return nil, fmt.Errorf("requires at-least one instruction to create a transaction")
   234  	}
   235  
   236  	options := transactionOptions{}
   237  	for _, opt := range opts {
   238  		opt.apply(&options)
   239  	}
   240  
   241  	feePayer := options.payer
   242  	if feePayer.IsZero() {
   243  		found := false
   244  		for _, act := range instructions[0].Accounts() {
   245  			if act.IsSigner {
   246  				feePayer = act.PublicKey
   247  				found = true
   248  				break
   249  			}
   250  		}
   251  		if !found {
   252  			return nil, fmt.Errorf("cannot determine fee payer. You can ether pass the fee payer via the 'TransactionWithInstructions' option parameter or it falls back to the first instruction's first signer")
   253  		}
   254  	}
   255  
   256  	addressLookupKeysMap := make(map[PublicKey]addressTablePubkeyWithIndex) // all accounts from tables as map
   257  	for addressTablePubKey, addressTable := range options.addressTables {
   258  		if len(addressTable) > 256 {
   259  			return nil, fmt.Errorf("max lookup table index exceeded for %s table", addressTablePubKey)
   260  		}
   261  
   262  		for i, address := range addressTable {
   263  			_, ok := addressLookupKeysMap[address]
   264  			if ok {
   265  				continue
   266  			}
   267  
   268  			addressLookupKeysMap[address] = addressTablePubkeyWithIndex{
   269  				addressTable: addressTablePubKey,
   270  				index:        uint8(i),
   271  			}
   272  		}
   273  	}
   274  
   275  	programIDs := make(PublicKeySlice, 0)
   276  	accounts := []*AccountMeta{}
   277  	for _, instruction := range instructions {
   278  		accounts = append(accounts, instruction.Accounts()...)
   279  		programIDs.UniqueAppend(instruction.ProgramID())
   280  	}
   281  
   282  	// for IsInvoked check
   283  	programIDsMap := make(map[PublicKey]struct{}, len(programIDs))
   284  	// Add programID to the account list
   285  	for _, programID := range programIDs {
   286  		accounts = append(accounts, &AccountMeta{
   287  			PublicKey:  programID,
   288  			IsSigner:   false,
   289  			IsWritable: false,
   290  		})
   291  
   292  		programIDsMap[programID] = struct{}{}
   293  	}
   294  
   295  	// Sort. Prioritizing first by signer, then by writable
   296  	sort.SliceStable(accounts, func(i, j int) bool {
   297  		return accounts[i].less(accounts[j])
   298  	})
   299  
   300  	uniqAccountsMap := map[PublicKey]uint64{}
   301  	uniqAccounts := []*AccountMeta{}
   302  	for _, acc := range accounts {
   303  		if index, found := uniqAccountsMap[acc.PublicKey]; found {
   304  			uniqAccounts[index].IsWritable = uniqAccounts[index].IsWritable || acc.IsWritable
   305  			continue
   306  		}
   307  		uniqAccounts = append(uniqAccounts, acc)
   308  		uniqAccountsMap[acc.PublicKey] = uint64(len(uniqAccounts) - 1)
   309  	}
   310  
   311  	if debugNewTransaction {
   312  		zlog.Debug("unique account sorted", zap.Int("account_count", len(uniqAccounts)))
   313  	}
   314  	// Move fee payer to the front
   315  	feePayerIndex := -1
   316  	for idx, acc := range uniqAccounts {
   317  		if acc.PublicKey.Equals(feePayer) {
   318  			feePayerIndex = idx
   319  		}
   320  	}
   321  	if debugNewTransaction {
   322  		zlog.Debug("current fee payer index", zap.Int("fee_payer_index", feePayerIndex))
   323  	}
   324  
   325  	accountCount := len(uniqAccounts)
   326  	if feePayerIndex < 0 {
   327  		// fee payer is not part of accounts we want to add it
   328  		accountCount++
   329  	}
   330  	allKeys := make([]*AccountMeta, accountCount)
   331  
   332  	itr := 1
   333  	for idx, uniqAccount := range uniqAccounts {
   334  		if idx == feePayerIndex {
   335  			uniqAccount.IsSigner = true
   336  			uniqAccount.IsWritable = true
   337  			allKeys[0] = uniqAccount
   338  			continue
   339  		}
   340  		allKeys[itr] = uniqAccount
   341  		itr++
   342  	}
   343  
   344  	if feePayerIndex < 0 {
   345  		// fee payer is not part of accounts we want to add it
   346  		feePayerAccount := &AccountMeta{
   347  			PublicKey:  feePayer,
   348  			IsSigner:   true,
   349  			IsWritable: true,
   350  		}
   351  		allKeys[0] = feePayerAccount
   352  	}
   353  
   354  	message := Message{
   355  		RecentBlockhash: recentBlockHash,
   356  	}
   357  	lookupsMap := make(map[PublicKey]struct { // extended MessageAddressTableLookup
   358  		AccountKey      PublicKey // The account key of the address table.
   359  		WritableIndexes []uint8
   360  		Writable        []PublicKey
   361  		ReadonlyIndexes []uint8
   362  		Readonly        []PublicKey
   363  	})
   364  	for idx, acc := range allKeys {
   365  
   366  		if debugNewTransaction {
   367  			zlog.Debug("transaction account",
   368  				zap.Int("account_index", idx),
   369  				zap.Stringer("account_pub_key", acc.PublicKey),
   370  			)
   371  		}
   372  
   373  		addressLookupKeyEntry, isPresentedInTables := addressLookupKeysMap[acc.PublicKey]
   374  		_, isInvoked := programIDsMap[acc.PublicKey]
   375  		// skip fee payer
   376  		if isPresentedInTables && idx != 0 && !acc.IsSigner && !isInvoked {
   377  			lookup := lookupsMap[addressLookupKeyEntry.addressTable]
   378  			if acc.IsWritable {
   379  				lookup.WritableIndexes = append(lookup.WritableIndexes, addressLookupKeyEntry.index)
   380  				lookup.Writable = append(lookup.Writable, acc.PublicKey)
   381  			} else {
   382  				lookup.ReadonlyIndexes = append(lookup.ReadonlyIndexes, addressLookupKeyEntry.index)
   383  				lookup.Readonly = append(lookup.Readonly, acc.PublicKey)
   384  			}
   385  
   386  			lookupsMap[addressLookupKeyEntry.addressTable] = lookup
   387  			continue // prevent changing message.Header properties
   388  		}
   389  
   390  		message.AccountKeys = append(message.AccountKeys, acc.PublicKey)
   391  
   392  		if acc.IsSigner {
   393  			message.Header.NumRequiredSignatures++
   394  			if !acc.IsWritable {
   395  				message.Header.NumReadonlySignedAccounts++
   396  			}
   397  			continue
   398  		}
   399  
   400  		if !acc.IsWritable {
   401  			message.Header.NumReadonlyUnsignedAccounts++
   402  		}
   403  	}
   404  
   405  	var lookupsWritableKeys []PublicKey
   406  	var lookupsReadOnlyKeys []PublicKey
   407  	if len(lookupsMap) > 0 {
   408  		lookups := make([]MessageAddressTableLookup, 0, len(lookupsMap))
   409  
   410  		for tablePubKey, l := range lookupsMap {
   411  			lookupsWritableKeys = append(lookupsWritableKeys, l.Writable...)
   412  			lookupsReadOnlyKeys = append(lookupsReadOnlyKeys, l.Readonly...)
   413  
   414  			lookups = append(lookups, MessageAddressTableLookup{
   415  				AccountKey:      tablePubKey,
   416  				WritableIndexes: l.WritableIndexes,
   417  				ReadonlyIndexes: l.ReadonlyIndexes,
   418  			})
   419  		}
   420  
   421  		// prevent error created in ResolveLookups
   422  		err := message.SetAddressTables(options.addressTables)
   423  		if err != nil {
   424  			return nil, fmt.Errorf("SetAddressTables: %s", err)
   425  		}
   426  		message.SetAddressTableLookups(lookups)
   427  	}
   428  
   429  	var idx uint16
   430  	accountKeyIndex := make(map[string]uint16, len(message.AccountKeys)+len(lookupsWritableKeys)+len(lookupsReadOnlyKeys))
   431  	for _, acc := range message.AccountKeys {
   432  		accountKeyIndex[acc.String()] = idx
   433  		idx++
   434  	}
   435  	for _, acc := range lookupsWritableKeys {
   436  		accountKeyIndex[acc.String()] = idx
   437  		idx++
   438  	}
   439  	for _, acc := range lookupsReadOnlyKeys {
   440  		accountKeyIndex[acc.String()] = idx
   441  		idx++
   442  	}
   443  
   444  	if debugNewTransaction {
   445  		zlog.Debug("message header compiled",
   446  			zap.Uint8("num_required_signatures", message.Header.NumRequiredSignatures),
   447  			zap.Uint8("num_readonly_signed_accounts", message.Header.NumReadonlySignedAccounts),
   448  			zap.Uint8("num_readonly_unsigned_accounts", message.Header.NumReadonlyUnsignedAccounts),
   449  		)
   450  	}
   451  
   452  	for txIdx, instruction := range instructions {
   453  		accounts = instruction.Accounts()
   454  		accountIndex := make([]uint16, len(accounts))
   455  		for idx, acc := range accounts {
   456  			accountIndex[idx] = accountKeyIndex[acc.PublicKey.String()]
   457  		}
   458  		data, err := instruction.Data()
   459  		if err != nil {
   460  			return nil, fmt.Errorf("unable to encode instructions [%d]: %w", txIdx, err)
   461  		}
   462  		message.Instructions = append(message.Instructions, CompiledInstruction{
   463  			ProgramIDIndex: accountKeyIndex[instruction.ProgramID().String()],
   464  			Accounts:       accountIndex,
   465  			Data:           data,
   466  		})
   467  	}
   468  
   469  	return &Transaction{
   470  		Message: message,
   471  	}, nil
   472  }
   473  
   474  type privateKeyGetter func(key PublicKey) *PrivateKey
   475  
   476  func (tx *Transaction) MarshalBinary() ([]byte, error) {
   477  	messageContent, err := tx.Message.MarshalBinary()
   478  	if err != nil {
   479  		return nil, fmt.Errorf("failed to encode tx.Message to binary: %w", err)
   480  	}
   481  
   482  	var signatureCount []byte
   483  	bin.EncodeCompactU16Length(&signatureCount, len(tx.Signatures))
   484  	output := make([]byte, 0, len(signatureCount)+len(signatureCount)*64+len(messageContent))
   485  	output = append(output, signatureCount...)
   486  	for _, sig := range tx.Signatures {
   487  		output = append(output, sig[:]...)
   488  	}
   489  	output = append(output, messageContent...)
   490  
   491  	return output, nil
   492  }
   493  
   494  func (tx Transaction) MarshalWithEncoder(encoder *bin.Encoder) error {
   495  	out, err := tx.MarshalBinary()
   496  	if err != nil {
   497  		return err
   498  	}
   499  	return encoder.WriteBytes(out, false)
   500  }
   501  
   502  func (tx *Transaction) UnmarshalWithDecoder(decoder *bin.Decoder) (err error) {
   503  	{
   504  		numSignatures, err := decoder.ReadCompactU16()
   505  		if err != nil {
   506  			return fmt.Errorf("unable to read numSignatures: %w", err)
   507  		}
   508  		if numSignatures < 0 {
   509  			return fmt.Errorf("numSignatures is negative")
   510  		}
   511  		if numSignatures > decoder.Remaining()/64 {
   512  			return fmt.Errorf("numSignatures %d is too large for remaining bytes %d", numSignatures, decoder.Remaining())
   513  		}
   514  
   515  		tx.Signatures = make([]Signature, numSignatures)
   516  		for i := 0; i < numSignatures; i++ {
   517  			_, err := decoder.Read(tx.Signatures[i][:])
   518  			if err != nil {
   519  				return fmt.Errorf("unable to read tx.Signatures[%d]: %w", i, err)
   520  			}
   521  		}
   522  	}
   523  	{
   524  		err := tx.Message.UnmarshalWithDecoder(decoder)
   525  		if err != nil {
   526  			return fmt.Errorf("unable to decode tx.Message: %w", err)
   527  		}
   528  	}
   529  	return nil
   530  }
   531  
   532  func (tx *Transaction) PartialSign(getter privateKeyGetter) (out []Signature, err error) {
   533  	messageContent, err := tx.Message.MarshalBinary()
   534  	if err != nil {
   535  		return nil, fmt.Errorf("unable to encode message for signing: %w", err)
   536  	}
   537  	signerKeys := tx.Message.signerKeys()
   538  
   539  	// Ensure that the transaction has the correct number of signatures initialized
   540  	if len(tx.Signatures) == 0 {
   541  		// Initialize the Signatures slice to the correct length if it's empty
   542  		tx.Signatures = make([]Signature, len(signerKeys))
   543  	} else if len(tx.Signatures) != len(signerKeys) {
   544  		// Return an error if the current length of the Signatures slice doesn't match the expected number
   545  		return nil, fmt.Errorf("invalid signatures length, expected %d, actual %d", len(signerKeys), len(tx.Signatures))
   546  	}
   547  
   548  	for i, key := range signerKeys {
   549  		privateKey := getter(key)
   550  		if privateKey != nil {
   551  			s, err := privateKey.Sign(messageContent)
   552  			if err != nil {
   553  				return nil, fmt.Errorf("failed to signed with key %q: %w", key.String(), err)
   554  			}
   555  			// Directly assign the signature to the corresponding position in the transaction's signature slice
   556  			tx.Signatures[i] = s
   557  		}
   558  	}
   559  	return tx.Signatures, nil
   560  }
   561  
   562  func (tx *Transaction) Sign(getter privateKeyGetter) (out []Signature, err error) {
   563  	signerKeys := tx.Message.signerKeys()
   564  	for _, key := range signerKeys {
   565  		if getter(key) == nil {
   566  			return nil, fmt.Errorf("signer key %q not found. Ensure all the signer keys are in the vault", key.String())
   567  		}
   568  	}
   569  	return tx.PartialSign(getter)
   570  }
   571  
   572  func (tx *Transaction) EncodeTree(encoder *text.TreeEncoder) (int, error) {
   573  	tx.EncodeToTree(encoder)
   574  	return encoder.WriteString(encoder.Tree.String())
   575  }
   576  
   577  // String returns a human-readable string representation of the transaction data.
   578  // To disable colors, set "github.com/gagliardetto/solana-go/text".DisableColors = true
   579  func (tx *Transaction) String() string {
   580  	buf := new(bytes.Buffer)
   581  	_, err := tx.EncodeTree(text.NewTreeEncoder(buf, ""))
   582  	if err != nil {
   583  		panic(err)
   584  	}
   585  	return buf.String()
   586  }
   587  
   588  func (tx Transaction) ToBase64() (string, error) {
   589  	out, err := tx.MarshalBinary()
   590  	if err != nil {
   591  		return "", err
   592  	}
   593  	return base64.StdEncoding.EncodeToString(out), nil
   594  }
   595  
   596  func (tx Transaction) MustToBase64() string {
   597  	out, err := tx.ToBase64()
   598  	if err != nil {
   599  		panic(err)
   600  	}
   601  	return out
   602  }
   603  
   604  func (tx *Transaction) EncodeToTree(parent treeout.Branches) {
   605  	parent.ParentFunc(func(txTree treeout.Branches) {
   606  		txTree.Child(fmt.Sprintf("Signatures[len=%v]", len(tx.Signatures))).ParentFunc(func(signaturesBranch treeout.Branches) {
   607  			for _, sig := range tx.Signatures {
   608  				signaturesBranch.Child(sig.String())
   609  			}
   610  		})
   611  
   612  		txTree.Child("Message").ParentFunc(func(messageBranch treeout.Branches) {
   613  			tx.Message.EncodeToTree(messageBranch)
   614  		})
   615  	})
   616  
   617  	parent.Child(fmt.Sprintf("Instructions[len=%v]", len(tx.Message.Instructions))).ParentFunc(func(message treeout.Branches) {
   618  		for _, inst := range tx.Message.Instructions {
   619  
   620  			progKey, err := tx.ResolveProgramIDIndex(inst.ProgramIDIndex)
   621  			if err == nil {
   622  				accounts, err := inst.ResolveInstructionAccounts(&tx.Message)
   623  				if err != nil {
   624  					message.Child(fmt.Sprintf(text.RedBG("cannot ResolveInstructionAccounts: %s"), err))
   625  					return
   626  				}
   627  				decodedInstruction, err := DecodeInstruction(progKey, accounts, inst.Data)
   628  				if err == nil {
   629  					if enToTree, ok := decodedInstruction.(text.EncodableToTree); ok {
   630  						enToTree.EncodeToTree(message)
   631  					} else {
   632  						message.Child(spew.Sdump(decodedInstruction))
   633  					}
   634  				} else {
   635  					// TODO: log error?
   636  					message.Child(fmt.Sprintf(text.RedBG("cannot decode instruction for %s program: %s"), progKey, err)).
   637  						Child(text.IndigoBG("Program") + ": " + text.Bold("<unknown>") + " " + text.ColorizeBG(progKey.String())).
   638  						//
   639  						ParentFunc(func(programBranch treeout.Branches) {
   640  							programBranch.Child(text.Purple(text.Bold("Instruction")) + ": " + text.Bold("<unknown>")).
   641  								//
   642  								ParentFunc(func(instructionBranch treeout.Branches) {
   643  									// Data of the instruction call:
   644  									instructionBranch.Child(text.Sf("data[len=%v bytes]", len(inst.Data))).ParentFunc(func(paramsBranch treeout.Branches) {
   645  										paramsBranch.Child(bin.FormatByteSlice(inst.Data))
   646  									})
   647  
   648  									// Accounts of the instruction call:
   649  									instructionBranch.Child(text.Sf("accounts[len=%v]", len(accounts))).ParentFunc(func(accountsBranch treeout.Branches) {
   650  										for i := range accounts {
   651  											accountsBranch.Child(formatMeta(text.Sf("accounts[%v]", i), accounts[i]))
   652  										}
   653  									})
   654  								})
   655  						})
   656  				}
   657  			} else {
   658  				message.Child(fmt.Sprintf(text.RedBG("cannot ResolveProgramIDIndex: %s"), err))
   659  			}
   660  		}
   661  	})
   662  }
   663  
   664  func formatMeta(name string, meta *AccountMeta) string {
   665  	if meta == nil {
   666  		return text.Shakespeare(name) + ": " + "<nil>"
   667  	}
   668  	out := text.Shakespeare(name) + ": " + text.ColorizeBG(meta.PublicKey.String())
   669  	out += " ["
   670  	if meta.IsWritable {
   671  		out += "WRITE"
   672  	}
   673  	if meta.IsSigner {
   674  		if meta.IsWritable {
   675  			out += ", "
   676  		}
   677  		out += "SIGN"
   678  	}
   679  	out += "] "
   680  	return out
   681  }
   682  
   683  // VerifySignatures verifies all the signatures in the transaction
   684  // against the pubkeys of the signers.
   685  func (tx *Transaction) VerifySignatures() error {
   686  	msg, err := tx.Message.MarshalBinary()
   687  	if err != nil {
   688  		return err
   689  	}
   690  
   691  	signers := tx.Message.Signers()
   692  
   693  	if len(signers) != len(tx.Signatures) {
   694  		return fmt.Errorf(
   695  			"got %v signers, but %v signatures",
   696  			len(signers),
   697  			len(tx.Signatures),
   698  		)
   699  	}
   700  
   701  	for i, sig := range tx.Signatures {
   702  		if !sig.Verify(signers[i], msg) {
   703  			return fmt.Errorf("invalid signature by %s", signers[i].String())
   704  		}
   705  	}
   706  
   707  	return nil
   708  }
   709  
   710  func (tx *Transaction) GetProgramIDs() (PublicKeySlice, error) {
   711  	programIDs := make(PublicKeySlice, 0)
   712  	for ixi, inst := range tx.Message.Instructions {
   713  		progKey, err := tx.ResolveProgramIDIndex(inst.ProgramIDIndex)
   714  		if err == nil {
   715  			programIDs = append(programIDs, progKey)
   716  		} else {
   717  			return nil, fmt.Errorf("cannot resolve program ID for instruction %d: %w", ixi, err)
   718  		}
   719  	}
   720  	return programIDs, nil
   721  }
   722  
   723  func (tx *Transaction) NumWriteableAccounts() int {
   724  	return countWriteableAccounts(tx)
   725  }
   726  
   727  func (tx *Transaction) NumSigners() int {
   728  	return countSigners(tx)
   729  }
   730  
   731  func countSigners(tx *Transaction) (count int) {
   732  	if tx == nil {
   733  		return -1
   734  	}
   735  	return tx.Message.Signers().Len()
   736  }
   737  
   738  func (tx *Transaction) NumReadonlyAccounts() int {
   739  	return countReadonlyAccounts(tx)
   740  }
   741  
   742  func countReadonlyAccounts(tx *Transaction) (count int) {
   743  	if tx == nil {
   744  		return -1
   745  	}
   746  	return int(tx.Message.Header.NumReadonlyUnsignedAccounts) + int(tx.Message.Header.NumReadonlySignedAccounts)
   747  }
   748  
   749  func countWriteableAccounts(tx *Transaction) (count int) {
   750  	if tx == nil {
   751  		return -1
   752  	}
   753  	if !tx.Message.IsVersioned() {
   754  		metas, err := tx.Message.AccountMetaList()
   755  		if err != nil {
   756  			return -1
   757  		}
   758  		for _, meta := range metas {
   759  			if meta.IsWritable {
   760  				count++
   761  			}
   762  		}
   763  		return count
   764  	}
   765  	numStatisKeys := len(tx.Message.AccountKeys)
   766  	statisKeys := tx.Message.AccountKeys
   767  	h := tx.Message.Header
   768  	for _, key := range statisKeys {
   769  		accIndex, ok := getStaticAccountIndex(tx, key)
   770  		if !ok {
   771  			continue
   772  		}
   773  		index := int(accIndex)
   774  		is := false
   775  		if index >= int(h.NumRequiredSignatures) {
   776  			// unsignedAccountIndex < numWritableUnsignedAccounts
   777  			is = index-int(h.NumRequiredSignatures) < (numStatisKeys-int(h.NumRequiredSignatures))-int(h.NumReadonlyUnsignedAccounts)
   778  		} else {
   779  			is = index < int(h.NumRequiredSignatures-h.NumReadonlySignedAccounts)
   780  		}
   781  		if is {
   782  			count++
   783  		}
   784  	}
   785  	if tx.Message.IsResolved() {
   786  		return count
   787  	}
   788  	count += tx.Message.NumWritableLookups()
   789  	return count
   790  }
   791  
   792  func getStaticAccountIndex(tx *Transaction, key PublicKey) (int, bool) {
   793  	for idx, a := range tx.Message.AccountKeys {
   794  		if a.Equals(key) {
   795  			return (idx), true
   796  		}
   797  	}
   798  	return -1, false
   799  }