@ -103,151 +103,62 @@ func makeTopics(query ...[]interface{}) ([][]common.Hash, error) {
return topics , nil
}
// Big batch of reflect types for topic reconstruction.
var (
reflectHash = reflect . TypeOf ( common . Hash { } )
reflectAddress = reflect . TypeOf ( common . Address { } )
reflectBigInt = reflect . TypeOf ( new ( big . Int ) )
)
// parseTopics converts the indexed topic fields into actual log field values.
//
// Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
// hashes as the topic value!
func parseTopics ( out interface { } , fields abi . Arguments , topics [ ] common . Hash ) error {
// Sanity check that the fields and topics match up
if len ( fields ) != len ( topics ) {
return errors . New ( "topic/field count mismatch" )
}
// Iterate over all the fields and reconstruct them from topics
for _ , arg := range fields {
if ! arg . Indexed {
return errors . New ( "non-indexed field in topic reconstruction" )
}
field := reflect . ValueOf ( out ) . Elem ( ) . FieldByName ( capitalise ( arg . Name ) )
// Try to parse the topic back into the fields based on primitive types
switch field . Kind ( ) {
case reflect . Bool :
if topics [ 0 ] [ common . HashLength - 1 ] == 1 {
field . Set ( reflect . ValueOf ( true ) )
}
case reflect . Int8 :
num := new ( big . Int ) . SetBytes ( topics [ 0 ] [ : ] )
field . Set ( reflect . ValueOf ( int8 ( num . Int64 ( ) ) ) )
case reflect . Int16 :
num := new ( big . Int ) . SetBytes ( topics [ 0 ] [ : ] )
field . Set ( reflect . ValueOf ( int16 ( num . Int64 ( ) ) ) )
case reflect . Int32 :
num := new ( big . Int ) . SetBytes ( topics [ 0 ] [ : ] )
field . Set ( reflect . ValueOf ( int32 ( num . Int64 ( ) ) ) )
case reflect . Int64 :
num := new ( big . Int ) . SetBytes ( topics [ 0 ] [ : ] )
field . Set ( reflect . ValueOf ( num . Int64 ( ) ) )
case reflect . Uint8 :
num := new ( big . Int ) . SetBytes ( topics [ 0 ] [ : ] )
field . Set ( reflect . ValueOf ( uint8 ( num . Uint64 ( ) ) ) )
case reflect . Uint16 :
num := new ( big . Int ) . SetBytes ( topics [ 0 ] [ : ] )
field . Set ( reflect . ValueOf ( uint16 ( num . Uint64 ( ) ) ) )
case reflect . Uint32 :
num := new ( big . Int ) . SetBytes ( topics [ 0 ] [ : ] )
field . Set ( reflect . ValueOf ( uint32 ( num . Uint64 ( ) ) ) )
case reflect . Uint64 :
num := new ( big . Int ) . SetBytes ( topics [ 0 ] [ : ] )
field . Set ( reflect . ValueOf ( num . Uint64 ( ) ) )
default :
// Ran out of plain primitive types, try custom types
switch field . Type ( ) {
case reflectHash : // Also covers all dynamic types
field . Set ( reflect . ValueOf ( topics [ 0 ] ) )
case reflectAddress :
var addr common . Address
copy ( addr [ : ] , topics [ 0 ] [ common . HashLength - common . AddressLength : ] )
field . Set ( reflect . ValueOf ( addr ) )
case reflectBigInt :
num := new ( big . Int ) . SetBytes ( topics [ 0 ] [ : ] )
if arg . Type . T == abi . IntTy {
if num . Cmp ( abi . MaxInt256 ) > 0 {
num . Add ( abi . MaxUint256 , big . NewInt ( 0 ) . Neg ( num ) )
num . Add ( num , big . NewInt ( 1 ) )
num . Neg ( num )
}
}
field . Set ( reflect . ValueOf ( num ) )
default :
// Ran out of custom types, try the crazies
switch {
// static byte array
case arg . Type . T == abi . FixedBytesTy :
reflect . Copy ( field , reflect . ValueOf ( topics [ 0 ] [ : arg . Type . Size ] ) )
default :
return fmt . Errorf ( "unsupported indexed type: %v" , arg . Type )
}
}
}
topics = topics [ 1 : ]
}
return nil
return parseTopicWithSetter ( fields , topics ,
func ( arg abi . Argument , reconstr interface { } ) {
field := reflect . ValueOf ( out ) . Elem ( ) . FieldByName ( capitalise ( arg . Name ) )
field . Set ( reflect . ValueOf ( reconstr ) )
} )
}
// parseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs
func parseTopicsIntoMap ( out map [ string ] interface { } , fields abi . Arguments , topics [ ] common . Hash ) error {
return parseTopicWithSetter ( fields , topics ,
func ( arg abi . Argument , reconstr interface { } ) {
out [ arg . Name ] = reconstr
} )
}
// parseTopicWithSetter converts the indexed topic field-value pairs and stores them using the
// provided set function.
//
// Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
// hashes as the topic value!
func parseTopicWithSetter ( fields abi . Arguments , topics [ ] common . Hash , setter func ( abi . Argument , interface { } ) ) error {
// Sanity check that the fields and topics match up
if len ( fields ) != len ( topics ) {
return errors . New ( "topic/field count mismatch" )
}
// Iterate over all the fields and reconstruct them from topics
for _ , arg := range fields {
for i , arg := range fields {
if ! arg . Indexed {
return errors . New ( "non-indexed field in topic reconstruction" )
}
var reconstr interface { }
switch arg . Type . T {
case abi . BoolTy :
out [ arg . Name ] = topics [ 0 ] [ common . HashLength - 1 ] == 1
case abi . IntTy , abi . UintTy :
out [ arg . Name ] = abi . ReadInteger ( arg . Type . T , arg . Type . Kind , topics [ 0 ] . Bytes ( ) )
case abi . AddressTy :
var addr common . Address
copy ( addr [ : ] , topics [ 0 ] [ common . HashLength - common . AddressLength : ] )
out [ arg . Name ] = addr
case abi . HashTy :
out [ arg . Name ] = topics [ 0 ]
case abi . FixedBytesTy :
array , err := abi . ReadFixedBytes ( arg . Type , topics [ 0 ] . Bytes ( ) )
if err != nil {
return err
}
out [ arg . Name ] = array
case abi . TupleTy :
return errors . New ( "tuple type in topic reconstruction" )
case abi . StringTy , abi . BytesTy , abi . SliceTy , abi . ArrayTy :
// Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash
// whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash
out [ arg . Name ] = topics [ 0 ]
reconstr = topics [ i ]
case abi . FunctionTy :
if garbage := binary . BigEndian . Uint64 ( topics [ 0 ] [ 0 : 8 ] ) ; garbage != 0 {
return fmt . Errorf ( "bind: got improperly encoded function type, got %v" , topics [ 0 ] . Bytes ( ) )
if garbage := binary . BigEndian . Uint64 ( topics [ i ] [ 0 : 8 ] ) ; garbage != 0 {
return fmt . Errorf ( "bind: got improperly encoded function type, got %v" , topics [ i ] . Bytes ( ) )
}
var tmp [ 24 ] byte
copy ( tmp [ : ] , topics [ 0 ] [ 8 : 32 ] )
out [ arg . Name ] = tmp
default : // Not handling tuples
return fmt . Errorf ( "unsupported indexed type: %v" , arg . Type )
copy ( tmp [ : ] , topics [ i ] [ 8 : 32 ] )
reconstr = tmp
default :
var err error
reconstr , err = abi . ToGoType ( 0 , arg . Type , topics [ i ] . Bytes ( ) )
if err != nil {
return err
}
}
topics = topics [ 1 : ]
// Use the setter function to store the value
setter ( arg , reconstr )
}
return nil