diff --git a/bchain/coins/eth/contract.go b/bchain/coins/eth/contract.go index 11fe89d0..ddc99294 100644 --- a/bchain/coins/eth/contract.go +++ b/bchain/coins/eth/contract.go @@ -21,6 +21,8 @@ const tokenTransferEventSignature = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f16 const tokenERC1155TransferSingleEventSignature = "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62" const tokenERC1155TransferBatchEventSignature = "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb" +const nameRegisteredEventSignature = "0xca6abbe9d7f11422cb6ca7629fbf6fe9efb1c621f71ce8f02b9f2a230097404f" + const contractNameSignature = "0x06fdde03" const contractSymbolSignature = "0x95d89b41" const contractDecimalsSignature = "0x313ce567" diff --git a/bchain/coins/eth/dataparser.go b/bchain/coins/eth/dataparser.go index 88043a96..a67c947e 100644 --- a/bchain/coins/eth/dataparser.go +++ b/bchain/coins/eth/dataparser.go @@ -163,16 +163,16 @@ func processParam(data string, index int, t *abi.Type, processed []bool) ([]stri } // get element count of dynamic type c, err := strconv.ParseInt(data[d:d+64], 16, 64) - count := int(c) if err != nil { return nil, 0, false } + count := int(c) processed[dynIndex] = true dynIndex++ if t.T == abi.StringTy || t.T == abi.BytesTy { d += 64 de := d + (count << 1) - if de > len(data) { + if de > len(data) || de < 0 { return nil, 0, false } if count == 0 { @@ -292,3 +292,27 @@ func ParseInputData(signatures *[]bchain.FourByteSignature, data string) *bchain } return &parsed } + +// getEnsRecord processes transaction log entry and tries to parse ENS record from it +func getEnsRecord(l *rpcLogWithTxHash) *bchain.AddressAliasRecord { + if len(l.Topics) == 3 && l.Topics[0] == nameRegisteredEventSignature && len(l.Data) >= 322 { + address, err := addressFromPaddedHex(l.Topics[2]) + if err != nil { + return nil + } + c, err := strconv.ParseInt(l.Data[194:194+64], 16, 64) + if err != nil { + return nil + } + de := 194 + 64 + (int(c) << 1) + if de > len(l.Data) || de < 0 { + return nil + } + b, err := hex.DecodeString(l.Data[194+64 : de]) + if err != nil { + return nil + } + return &bchain.AddressAliasRecord{Address: address, Name: string(b)} + } + return nil +} diff --git a/bchain/coins/eth/dataparser_test.go b/bchain/coins/eth/dataparser_test.go index 234dd8cd..5aca77ec 100644 --- a/bchain/coins/eth/dataparser_test.go +++ b/bchain/coins/eth/dataparser_test.go @@ -367,3 +367,79 @@ func TestParseInputData(t *testing.T) { }) } } + +func Test_getEnsRecord(t *testing.T) { + tests := []struct { + name string + log rpcLogWithTxHash + want *bchain.AddressAliasRecord + }{ + { + name: "unraveled", + log: rpcLogWithTxHash{ + RpcLog: bchain.RpcLog{ + Address: "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5", + Topics: []string{ + "0xca6abbe9d7f11422cb6ca7629fbf6fe9efb1c621f71ce8f02b9f2a230097404f", + "0x40ce2aa8cd9ee9fef4bf3a68abab7fbcceb6bac89370518caf6a602cefe836bd", + "0x0000000000000000000000002c630b16aa53ae0189880e15c23323688acb607c", + }, + Data: "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000017629245f5a86f0000000000000000000000000000000000000000000000000000000069dbb21d0000000000000000000000000000000000000000000000000000000000000009756e726176656c65640000000000000000000000000000000000000000000000", + }, + }, + want: &bchain.AddressAliasRecord{Address: "0x2C630b16Aa53ae0189880e15C23323688acb607c", Name: "unraveled"}, + }, + { + name: "4x unraveled", + log: rpcLogWithTxHash{ + RpcLog: bchain.RpcLog{ + Address: "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5", + Topics: []string{ + "0xca6abbe9d7f11422cb6ca7629fbf6fe9efb1c621f71ce8f02b9f2a230097404f", + "0x40ce2aa8cd9ee9fef4bf3a68abab7fbcceb6bac89370518caf6a602cefe836bd", + "0x0000000000000000000000002c630b16aa53ae0189880e15c23323688acb607c", + }, + Data: "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000017629245f5a86f0000000000000000000000000000000000000000000000000000000069dbb21d0000000000000000000000000000000000000000000000000000000000000024756e726176656c6564756e726176656c6564756e726176656c6564756e726176656c656400000000000000000000000000000000000000000000000000000000", + }, + }, + want: &bchain.AddressAliasRecord{Address: "0x2C630b16Aa53ae0189880e15C23323688acb607c", Name: "unraveledunraveledunraveledunraveled"}, + }, + { + name: "no signature", + log: rpcLogWithTxHash{ + RpcLog: bchain.RpcLog{ + Address: "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5", + Topics: []string{ + "0xca6abbe9d7f11422cb6ca7629fbf6fe9efb1c621f71ce8f02b9f2a230097404e", + "0x40ce2aa8cd9ee9fef4bf3a68abab7fbcceb6bac89370518caf6a602cefe836bd", + "0x0000000000000000000000002c630b16aa53ae0189880e15c23323688acb607c", + }, + Data: "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000017629245f5a86f0000000000000000000000000000000000000000000000000000000069dbb21d0000000000000000000000000000000000000000000000000000000000000009756e726176656c65640000000000000000000000000000000000000000000000", + }, + }, + want: nil, + }, + { + name: "name length does not match", + log: rpcLogWithTxHash{ + RpcLog: bchain.RpcLog{ + Address: "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5", + Topics: []string{ + "0xca6abbe9d7f11422cb6ca7629fbf6fe9efb1c621f71ce8f02b9f2a230097404f", + "0x40ce2aa8cd9ee9fef4bf3a68abab7fbcceb6bac89370518caf6a602cefe836bd", + "0x0000000000000000000000002c630b16aa53ae0189880e15c23323688acb607c", + }, + Data: "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000017629245f5a86f0000000000000000000000000000000000000000000000000000000069dbb21d0000000000000000000000000000000000000000000000000000000000000ff9756e726176656c65640000000000000000000000000000000000000000000000", + }, + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getEnsRecord(&tt.log); !reflect.DeepEqual(got, tt.want) { + t.Errorf("getEnsRecord() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 8c4630c2..7d5b6770 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -497,24 +497,28 @@ func (b *EthereumRPC) getBlockRaw(hash string, height uint32, fullTxs bool) (jso return raw, nil } -func (b *EthereumRPC) getTokenTransferEventsForBlock(blockNumber string) (map[string][]*bchain.RpcLog, error) { +func (b *EthereumRPC) processEventsForBlock(blockNumber string) (map[string][]*bchain.RpcLog, []bchain.AddressAliasRecord, error) { ctx, cancel := context.WithTimeout(context.Background(), b.timeout) defer cancel() var logs []rpcLogWithTxHash + var ensRecords []bchain.AddressAliasRecord err := b.rpc.CallContext(ctx, &logs, "eth_getLogs", map[string]interface{}{ "fromBlock": blockNumber, "toBlock": blockNumber, - // "topics": []string{tokenTransferEventSignature, tokenERC1155TransferSingleEventSignature, tokenERC1155TransferBatchEventSignature}, }) if err != nil { - return nil, errors.Annotatef(err, "blockNumber %v", blockNumber) + return nil, nil, errors.Annotatef(err, "blockNumber %v", blockNumber) } r := make(map[string][]*bchain.RpcLog) for i := range logs { l := &logs[i] r[l.Hash] = append(r[l.Hash], &l.RpcLog) + ens := getEnsRecord(l) + if ens != nil { + ensRecords = append(ensRecords, *ens) + } } - return r, nil + return r, ensRecords, nil } type rpcCallTrace struct { @@ -636,17 +640,24 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error if err != nil { return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) } - // get contract transfers events - logs, err := b.getTokenTransferEventsForBlock(head.Number) + // get block events + logs, ens, err := b.processEventsForBlock(head.Number) if err != nil { return nil, err } // error fetching internal data does not stop the block processing var blockSpecificData *bchain.EthereumBlockSpecificData internalData, err := b.getInternalDataForBlock(head.Hash, body.Transactions) - if err != nil { - blockSpecificData = &bchain.EthereumBlockSpecificData{InternalDataError: err.Error()} - glog.Info("InternalDataError ", bbh.Height, ": ", err.Error()) + if err != nil || len(ens) > 0 { + blockSpecificData = &bchain.EthereumBlockSpecificData{} + if err != nil { + blockSpecificData.InternalDataError = err.Error() + glog.Info("InternalDataError ", bbh.Height, ": ", err.Error()) + } + if len(ens) > 0 { + blockSpecificData.AddressAliasRecords = ens + glog.Info("ENS", ens) + } } btxs := make([]bchain.Tx, len(body.Transactions)) diff --git a/bchain/types_ethereum_type.go b/bchain/types_ethereum_type.go index a6315324..357f1667 100644 --- a/bchain/types_ethereum_type.go +++ b/bchain/types_ethereum_type.go @@ -121,12 +121,21 @@ type RpcReceipt struct { Logs []*RpcLog `json:"logs"` } +// EthereumSpecificData contains data specific to Ethereum transactions type EthereumSpecificData struct { Tx *RpcTransaction `json:"tx"` InternalData *EthereumInternalData `json:"internalData,omitempty"` Receipt *RpcReceipt `json:"receipt,omitempty"` } -type EthereumBlockSpecificData struct { - InternalDataError string +// AddressAliasRecord maps address to ENS name +type AddressAliasRecord struct { + Address string + Name string +} + +// EthereumBlockSpecificData contain data specific for Ethereum block +type EthereumBlockSpecificData struct { + InternalDataError string + AddressAliasRecords []AddressAliasRecord }