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 }