github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/accounts/abi/bind/topics.go (about)

     1  //  Copyright 2018 The go-ethereum Authors
     2  //  Copyright 2019 The go-aigar Authors
     3  //  This file is part of the go-aigar library.
     4  //
     5  //  The go-aigar library is free software: you can redistribute it and/or modify
     6  //  it under the terms of the GNU Lesser General Public License as published by
     7  //  the Free Software Foundation, either version 3 of the License, or
     8  //  (at your option) any later version.
     9  //
    10  //  The go-aigar library is distributed in the hope that it will be useful,
    11  //  but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  //  GNU Lesser General Public License for more details.
    14  //
    15  //  You should have received a copy of the GNU Lesser General Public License
    16  //  along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package bind
    19  
    20  import (
    21  	"encoding/binary"
    22  	"errors"
    23  	"fmt"
    24  	"math/big"
    25  	"reflect"
    26  
    27  	"github.com/AigarNetwork/aigar/accounts/abi"
    28  	"github.com/AigarNetwork/aigar/common"
    29  	"github.com/AigarNetwork/aigar/crypto"
    30  )
    31  
    32  // makeTopics converts a filter query argument list into a filter topic set.
    33  func makeTopics(query ...[]interface{}) ([][]common.Hash, error) {
    34  	topics := make([][]common.Hash, len(query))
    35  	for i, filter := range query {
    36  		for _, rule := range filter {
    37  			var topic common.Hash
    38  
    39  			// Try to generate the topic based on simple types
    40  			switch rule := rule.(type) {
    41  			case common.Hash:
    42  				copy(topic[:], rule[:])
    43  			case common.Address:
    44  				copy(topic[common.HashLength-common.AddressLength:], rule[:])
    45  			case *big.Int:
    46  				blob := rule.Bytes()
    47  				copy(topic[common.HashLength-len(blob):], blob)
    48  			case bool:
    49  				if rule {
    50  					topic[common.HashLength-1] = 1
    51  				}
    52  			case int8:
    53  				blob := big.NewInt(int64(rule)).Bytes()
    54  				copy(topic[common.HashLength-len(blob):], blob)
    55  			case int16:
    56  				blob := big.NewInt(int64(rule)).Bytes()
    57  				copy(topic[common.HashLength-len(blob):], blob)
    58  			case int32:
    59  				blob := big.NewInt(int64(rule)).Bytes()
    60  				copy(topic[common.HashLength-len(blob):], blob)
    61  			case int64:
    62  				blob := big.NewInt(rule).Bytes()
    63  				copy(topic[common.HashLength-len(blob):], blob)
    64  			case uint8:
    65  				blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
    66  				copy(topic[common.HashLength-len(blob):], blob)
    67  			case uint16:
    68  				blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
    69  				copy(topic[common.HashLength-len(blob):], blob)
    70  			case uint32:
    71  				blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
    72  				copy(topic[common.HashLength-len(blob):], blob)
    73  			case uint64:
    74  				blob := new(big.Int).SetUint64(rule).Bytes()
    75  				copy(topic[common.HashLength-len(blob):], blob)
    76  			case string:
    77  				hash := crypto.Keccak256Hash([]byte(rule))
    78  				copy(topic[:], hash[:])
    79  			case []byte:
    80  				hash := crypto.Keccak256Hash(rule)
    81  				copy(topic[:], hash[:])
    82  
    83  			default:
    84  				// todo(rjl493456442) according solidity documentation, indexed event
    85  				// parameters that are not value types i.e. arrays and structs are not
    86  				// stored directly but instead a keccak256-hash of an encoding is stored.
    87  				//
    88  				// We only convert stringS and bytes to hash, still need to deal with
    89  				// array(both fixed-size and dynamic-size) and struct.
    90  
    91  				// Attempt to generate the topic from funky types
    92  				val := reflect.ValueOf(rule)
    93  				switch {
    94  				// static byte array
    95  				case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
    96  					reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val)
    97  				default:
    98  					return nil, fmt.Errorf("unsupported indexed type: %T", rule)
    99  				}
   100  			}
   101  			topics[i] = append(topics[i], topic)
   102  		}
   103  	}
   104  	return topics, nil
   105  }
   106  
   107  // Big batch of reflect types for topic reconstruction.
   108  var (
   109  	reflectHash    = reflect.TypeOf(common.Hash{})
   110  	reflectAddress = reflect.TypeOf(common.Address{})
   111  	reflectBigInt  = reflect.TypeOf(new(big.Int))
   112  )
   113  
   114  // parseTopics converts the indexed topic fields into actual log field values.
   115  //
   116  // Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
   117  // hashes as the topic value!
   118  func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) error {
   119  	// Sanity check that the fields and topics match up
   120  	if len(fields) != len(topics) {
   121  		return errors.New("topic/field count mismatch")
   122  	}
   123  	// Iterate over all the fields and reconstruct them from topics
   124  	for _, arg := range fields {
   125  		if !arg.Indexed {
   126  			return errors.New("non-indexed field in topic reconstruction")
   127  		}
   128  		field := reflect.ValueOf(out).Elem().FieldByName(capitalise(arg.Name))
   129  
   130  		// Try to parse the topic back into the fields based on primitive types
   131  		switch field.Kind() {
   132  		case reflect.Bool:
   133  			if topics[0][common.HashLength-1] == 1 {
   134  				field.Set(reflect.ValueOf(true))
   135  			}
   136  		case reflect.Int8:
   137  			num := new(big.Int).SetBytes(topics[0][:])
   138  			field.Set(reflect.ValueOf(int8(num.Int64())))
   139  
   140  		case reflect.Int16:
   141  			num := new(big.Int).SetBytes(topics[0][:])
   142  			field.Set(reflect.ValueOf(int16(num.Int64())))
   143  
   144  		case reflect.Int32:
   145  			num := new(big.Int).SetBytes(topics[0][:])
   146  			field.Set(reflect.ValueOf(int32(num.Int64())))
   147  
   148  		case reflect.Int64:
   149  			num := new(big.Int).SetBytes(topics[0][:])
   150  			field.Set(reflect.ValueOf(num.Int64()))
   151  
   152  		case reflect.Uint8:
   153  			num := new(big.Int).SetBytes(topics[0][:])
   154  			field.Set(reflect.ValueOf(uint8(num.Uint64())))
   155  
   156  		case reflect.Uint16:
   157  			num := new(big.Int).SetBytes(topics[0][:])
   158  			field.Set(reflect.ValueOf(uint16(num.Uint64())))
   159  
   160  		case reflect.Uint32:
   161  			num := new(big.Int).SetBytes(topics[0][:])
   162  			field.Set(reflect.ValueOf(uint32(num.Uint64())))
   163  
   164  		case reflect.Uint64:
   165  			num := new(big.Int).SetBytes(topics[0][:])
   166  			field.Set(reflect.ValueOf(num.Uint64()))
   167  
   168  		default:
   169  			// Ran out of plain primitive types, try custom types
   170  
   171  			switch field.Type() {
   172  			case reflectHash: // Also covers all dynamic types
   173  				field.Set(reflect.ValueOf(topics[0]))
   174  
   175  			case reflectAddress:
   176  				var addr common.Address
   177  				copy(addr[:], topics[0][common.HashLength-common.AddressLength:])
   178  				field.Set(reflect.ValueOf(addr))
   179  
   180  			case reflectBigInt:
   181  				num := new(big.Int).SetBytes(topics[0][:])
   182  				field.Set(reflect.ValueOf(num))
   183  
   184  			default:
   185  				// Ran out of custom types, try the crazies
   186  				switch {
   187  				// static byte array
   188  				case arg.Type.T == abi.FixedBytesTy:
   189  					reflect.Copy(field, reflect.ValueOf(topics[0][:arg.Type.Size]))
   190  				default:
   191  					return fmt.Errorf("unsupported indexed type: %v", arg.Type)
   192  				}
   193  			}
   194  		}
   195  		topics = topics[1:]
   196  	}
   197  	return nil
   198  }
   199  
   200  // parseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs
   201  func parseTopicsIntoMap(out map[string]interface{}, fields abi.Arguments, topics []common.Hash) error {
   202  	// Sanity check that the fields and topics match up
   203  	if len(fields) != len(topics) {
   204  		return errors.New("topic/field count mismatch")
   205  	}
   206  	// Iterate over all the fields and reconstruct them from topics
   207  	for _, arg := range fields {
   208  		if !arg.Indexed {
   209  			return errors.New("non-indexed field in topic reconstruction")
   210  		}
   211  
   212  		switch arg.Type.T {
   213  		case abi.BoolTy:
   214  			out[arg.Name] = topics[0][common.HashLength-1] == 1
   215  		case abi.IntTy, abi.UintTy:
   216  			num := new(big.Int).SetBytes(topics[0][:])
   217  			out[arg.Name] = num
   218  		case abi.AddressTy:
   219  			var addr common.Address
   220  			copy(addr[:], topics[0][common.HashLength-common.AddressLength:])
   221  			out[arg.Name] = addr
   222  		case abi.HashTy:
   223  			out[arg.Name] = topics[0]
   224  		case abi.FixedBytesTy:
   225  			out[arg.Name] = topics[0][:]
   226  		case abi.StringTy, abi.BytesTy, abi.SliceTy, abi.ArrayTy:
   227  			// Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash
   228  			// whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash
   229  			out[arg.Name] = topics[0]
   230  		case abi.FunctionTy:
   231  			if garbage := binary.BigEndian.Uint64(topics[0][0:8]); garbage != 0 {
   232  				return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[0].Bytes())
   233  			}
   234  			var tmp [24]byte
   235  			copy(tmp[:], topics[0][8:32])
   236  			out[arg.Name] = tmp
   237  		default: // Not handling tuples
   238  			return fmt.Errorf("unsupported indexed type: %v", arg.Type)
   239  		}
   240  
   241  		topics = topics[1:]
   242  	}
   243  
   244  	return nil
   245  }