github.com/gagliardetto/solana-go@v1.11.0/registry.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 "errors" 22 "fmt" 23 "reflect" 24 "sync" 25 ) 26 27 var ErrInstructionDecoderNotFound = errors.New("instruction decoder not found") 28 29 // InstructionDecoder receives the AccountMeta FOR THAT INSTRUCTION, 30 // and not the accounts of the *Message object. Resolve with 31 // CompiledInstruction.ResolveInstructionAccounts(message) beforehand. 32 type InstructionDecoder func(instructionAccounts []*AccountMeta, data []byte) (interface{}, error) 33 34 var instructionDecoderRegistry = newInstructionDecoderRegistry() 35 36 type decoderRegistry struct { 37 mu *sync.RWMutex 38 decoders map[PublicKey]InstructionDecoder 39 } 40 41 func newInstructionDecoderRegistry() *decoderRegistry { 42 return &decoderRegistry{ 43 mu: &sync.RWMutex{}, 44 decoders: make(map[PublicKey]InstructionDecoder), 45 } 46 } 47 48 func (reg *decoderRegistry) Has(programID PublicKey) bool { 49 reg.mu.RLock() 50 defer reg.mu.RUnlock() 51 52 _, ok := reg.decoders[programID] 53 return ok 54 } 55 56 func (reg *decoderRegistry) Get(programID PublicKey) (InstructionDecoder, bool) { 57 reg.mu.RLock() 58 defer reg.mu.RUnlock() 59 60 decoder, ok := reg.decoders[programID] 61 return decoder, ok 62 } 63 64 // RegisterIfNew registers the provided decoder for the provided programID ONLY if there isn't 65 // already a registered decoder for the programID. 66 // Returns true if was successfully registered right now (non-previously registered); 67 // returns false if there already was a decoder registered. 68 func (reg *decoderRegistry) RegisterIfNew(programID PublicKey, decoder InstructionDecoder) bool { 69 reg.mu.Lock() 70 defer reg.mu.Unlock() 71 72 _, ok := reg.decoders[programID] 73 if ok { 74 return false 75 } 76 reg.decoders[programID] = decoder 77 return true 78 } 79 80 func RegisterInstructionDecoder(programID PublicKey, decoder InstructionDecoder) { 81 prev, has := instructionDecoderRegistry.Get(programID) 82 if has { 83 // If it's the same function, then OK (tollerate multiple calls with same params). 84 if isSameFunction(prev, decoder) { 85 return 86 } 87 // If it's another decoder for the same pubkey, then panic. 88 panic(fmt.Sprintf("unable to re-register instruction decoder for program %s", programID)) 89 } 90 instructionDecoderRegistry.RegisterIfNew(programID, decoder) 91 } 92 93 func isSameFunction(f1 interface{}, f2 interface{}) bool { 94 return reflect.ValueOf(f1).Pointer() == reflect.ValueOf(f2).Pointer() 95 } 96 97 func DecodeInstruction(programID PublicKey, accounts []*AccountMeta, data []byte) (interface{}, error) { 98 decoder, found := instructionDecoderRegistry.Get(programID) 99 if !found { 100 return nil, ErrInstructionDecoderNotFound 101 } 102 return decoder(accounts, data) 103 }