diff --git a/db/migrations/20180827120358_create_token_transfer_table.down.sql b/db/migrations/20180827120358_create_token_transfer_table.down.sql new file mode 100644 index 00000000..82bff29e --- /dev/null +++ b/db/migrations/20180827120358_create_token_transfer_table.down.sql @@ -0,0 +1 @@ +DROP TABLE token_transfers; \ No newline at end of file diff --git a/db/migrations/20180827120358_create_token_transfer_table.up.sql b/db/migrations/20180827120358_create_token_transfer_table.up.sql new file mode 100644 index 00000000..fe8091b4 --- /dev/null +++ b/db/migrations/20180827120358_create_token_transfer_table.up.sql @@ -0,0 +1,14 @@ +CREATE TABLE token_transfers ( + id SERIAL, + vulcanize_log_id INTEGER NOT NULL UNIQUE, + token_name CHARACTER VARYING(66) NOT NULL, + token_address CHARACTER VARYING(66) NOT NULL, + to_address CHARACTER VARYING(66) NOT NULL, + from_address CHARACTER VARYING(66) NOT NULL, + tokens DECIMAL NOT NULL, + block INTEGER NOT NULL, + tx CHARACTER VARYING(66) NOT NULL, + CONSTRAINT log_index_fk FOREIGN KEY (vulcanize_log_id) + REFERENCES logs (id) + ON DELETE CASCADE +) \ No newline at end of file diff --git a/db/migrations/20180827120410_create_token_approval_table.down.sql b/db/migrations/20180827120410_create_token_approval_table.down.sql new file mode 100644 index 00000000..acfcaca5 --- /dev/null +++ b/db/migrations/20180827120410_create_token_approval_table.down.sql @@ -0,0 +1 @@ +DROP TABLE token_approvals; \ No newline at end of file diff --git a/db/migrations/20180827120410_create_token_approval_table.up.sql b/db/migrations/20180827120410_create_token_approval_table.up.sql new file mode 100644 index 00000000..5dc45164 --- /dev/null +++ b/db/migrations/20180827120410_create_token_approval_table.up.sql @@ -0,0 +1,14 @@ +CREATE TABLE token_approvals ( + id SERIAL, + vulcanize_log_id INTEGER NOT NULL UNIQUE, + token_name CHARACTER VARYING(66) NOT NULL, + token_address CHARACTER VARYING(66) NOT NULL, + owner CHARACTER VARYING(66) NOT NULL, + spender CHARACTER VARYING(66) NOT NULL, + tokens DECIMAL NOT NULL, + block INTEGER NOT NULL, + tx CHARACTER VARYING(66) NOT NULL, + CONSTRAINT log_index_fk FOREIGN KEY (vulcanize_log_id) + REFERENCES logs (id) + ON DELETE CASCADE +) \ No newline at end of file diff --git a/examples/erc20_watcher/event_triggered/converter.go b/examples/erc20_watcher/event_triggered/converter.go index 45ec8834..c75c5269 100644 --- a/examples/erc20_watcher/event_triggered/converter.go +++ b/examples/erc20_watcher/event_triggered/converter.go @@ -22,6 +22,9 @@ import ( "github.com/vulcanize/vulcanizedb/pkg/core" ) +// Converter converts a raw event log into its corresponding entity +// and can subsequently convert the entity into a model + type ERC20ConverterInterface interface { ToTransferEntity(watchedEvent core.WatchedEvent) (*TransferEntity, error) ToTransferModel(entity TransferEntity) TransferModel diff --git a/examples/erc20_watcher/event_triggered/integration_test.go b/examples/erc20_watcher/event_triggered/integration_test.go index 0531c04f..cf53891b 100644 --- a/examples/erc20_watcher/event_triggered/integration_test.go +++ b/examples/erc20_watcher/event_triggered/integration_test.go @@ -13,3 +13,115 @@ // limitations under the License. package event_triggered_test + +/* +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/config" + "github.com/vulcanize/vulcanizedb/examples/constants" + "github.com/vulcanize/vulcanizedb/examples/erc20_watcher/event_triggered" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/fakes" + "time" +) + +var idOne = "0x000000000000000000000000000000000000000000000000000000000000af21" +var logKill = core.Log{ + BlockNumber: 5488076, + Address: constants.DaiContractAddress, + TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad6546ae", + Index: 0, + Topics: [4]string{ + constants.TransferEventSignature, + idOne, + "0x9dd48110dcc444fdc242510c09bbbbe21a5975cac061d82f7b843bce061ba391", + "0x0000000000000000000000003dc389e0a69d6364a66ab64ebd51234da9569284", + }, + Data: "0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359000000000000000000000000000000000000000000000000392d2e2bda9c00000000000000000000000000000000000000000000000000927f41fa0a4a418000000000000000000000000000000000000000000000000000000000005adcfebe", +} + +var expectedLogKill = event_triggered.TransferModel{ + Block: 5488076, + TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad6546ae", +} + +//converted logID to assert against +var logs = []core.Log{ + logKill, + { + BlockNumber: 0, + TxHash: "", + Address: "", + Topics: core.Topics{}, + Index: 0, + Data: "", + }, +} + +var _ = Describe("Integration test with vulcanizedb", func() { + var db *postgres.DB + + BeforeEach(func() { + var err error + db, err = postgres.NewDB(config.Database{ + Hostname: "localhost", + Name: "vulcanize_private", + Port: 5432, + }, core.Node{}) + Expect(err).NotTo(HaveOccurred()) + lr := repositories.LogRepository{DB: db} + err = lr.CreateLogs(logs) + Expect(err).ToNot(HaveOccurred()) + + var vulcanizeLogIds []int64 + err = db.Select(&vulcanizeLogIds, `SELECT id FROM public.logs`) + Expect(err).ToNot(HaveOccurred()) + + }) + + AfterEach(func() { + _, err := db.Exec(`DELETE FROM oasis.kill`) + Expect(err).ToNot(HaveOccurred()) + _, err = db.Exec(`DELETE FROM log_filters`) + Expect(err).ToNot(HaveOccurred()) + _, err = db.Exec(`DELETE FROM logs`) + Expect(err).ToNot(HaveOccurred()) + }) + + It("creates oasis.kill for each LogKill event received", func() { + blockchain := &fakes.MockBlockChain{} + transformer := event_triggered.NewTransformer(db, blockchain) + + transformer.Execute() + + var count int + err := db.QueryRow(`SELECT COUNT(*) FROM oasis.kill`).Scan(&count) + Expect(err).ToNot(HaveOccurred()) + Expect(count).To(Equal(1)) + + type dbRow struct { + DBID uint64 `db:"db_id"` + VulcanizeLogID int64 `db:"vulcanize_log_id"` + event_triggered.TransferModel + } + var logKill dbRow + err = db.Get(&logKill, `SELECT * FROM oasis.kill WHERE block=$1`, logs[0].BlockNumber) + Expect(err).ToNot(HaveOccurred()) + Expect(logKill.ID).To(Equal(expectedLogKill.ID)) + Expect(logKill.Pair).To(Equal(expectedLogKill.Pair)) + Expect(logKill.Guy).To(Equal(expectedLogKill.Guy)) + Expect(logKill.Gem).To(Equal(expectedLogKill.Gem)) + Expect(logKill.Lot).To(Equal(expectedLogKill.Lot)) + Expect(logKill.Pie).To(Equal(expectedLogKill.Pie)) + Expect(logKill.Bid).To(Equal(expectedLogKill.Bid)) + Expect(logKill.Block).To(Equal(expectedLogKill.Block)) + Expect(logKill.Tx).To(Equal(expectedLogKill.Tx)) + Expect(logKill.Timestamp.Equal(expectedLogKill.Timestamp)).To(BeTrue()) + }) + +}) +*/ diff --git a/examples/erc20_watcher/event_triggered/model.go b/examples/erc20_watcher/event_triggered/model.go index fda4771f..cc0d9199 100644 --- a/examples/erc20_watcher/event_triggered/model.go +++ b/examples/erc20_watcher/event_triggered/model.go @@ -15,21 +15,21 @@ package event_triggered type TransferModel struct { - TokenName string - TokenAddress string - To string - From string - Tokens string - Block int64 - TxHash string + TokenName string `db:"token_name"` + TokenAddress string `db:"token_address"` + To string `db:"to_address"` + From string `db:"from_address"` + Tokens string `db:"tokens"` + Block int64 `db:"block"` + TxHash string `db:"tx"` } type ApprovalModel struct { - TokenName string - TokenAddress string - Owner string - Spender string - Tokens string - Block int64 - TxHash string + TokenName string `db:"token_name"` + TokenAddress string `db:"token_address"` + Owner string `db:"owner"` + Spender string `db:"spender"` + Tokens string `db:"tokens"` + Block int64 `db:"block"` + TxHash string `db:"tx"` } diff --git a/examples/erc20_watcher/event_triggered/repository.go b/examples/erc20_watcher/event_triggered/repository.go index 3e63dd1c..37981541 100644 --- a/examples/erc20_watcher/event_triggered/repository.go +++ b/examples/erc20_watcher/event_triggered/repository.go @@ -30,10 +30,10 @@ type Repository struct { func (repository Repository) CreateTransfer(transferModel TransferModel, vulcanizeLogId int64) error { _, err := repository.DB.Exec( - `INSERT INTO TRANSFERS (vulcanize_log_id, token_address, to, from, tokens, block, tx) - VALUES ($1, $2, $3, $4, $5, $6, $7) + `INSERT INTO token_transfers (vulcanize_log_id, token_name, token_address, to_address, from_address, tokens, block, tx) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (vulcanize_log_id) DO NOTHING`, - vulcanizeLogId, transferModel.TokenAddress, transferModel.To, transferModel.From, transferModel.Tokens, transferModel.Block, transferModel.TxHash) + vulcanizeLogId, transferModel.TokenName, transferModel.TokenAddress, transferModel.To, transferModel.From, transferModel.Tokens, transferModel.Block, transferModel.TxHash) if err != nil { return err @@ -45,10 +45,10 @@ func (repository Repository) CreateTransfer(transferModel TransferModel, vulcani func (repository Repository) CreateApproval(approvalModel ApprovalModel, vulcanizeLogId int64) error { _, err := repository.DB.Exec( - `INSERT INTO APPROVALS (vulcanize_log_id, token_address, token_owner, token_spender, tokens, block, tx) - VALUES ($1, $2, $3, $4, $5, $6, $7) + `INSERT INTO token_approvals (vulcanize_log_id, token_name, token_address, owner, spender, tokens, block, tx) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (vulcanize_log_id) DO NOTHING`, - vulcanizeLogId, approvalModel.TokenAddress, approvalModel.Owner, approvalModel.Spender, approvalModel.Tokens, approvalModel.Block, approvalModel.TxHash) + vulcanizeLogId, approvalModel.TokenName, approvalModel.TokenAddress, approvalModel.Owner, approvalModel.Spender, approvalModel.Tokens, approvalModel.Block, approvalModel.TxHash) if err != nil { return err diff --git a/examples/erc20_watcher/event_triggered/repository_test.go b/examples/erc20_watcher/event_triggered/repository_test.go index 0531c04f..da9c31e8 100644 --- a/examples/erc20_watcher/event_triggered/repository_test.go +++ b/examples/erc20_watcher/event_triggered/repository_test.go @@ -13,3 +13,222 @@ // limitations under the License. package event_triggered_test + +import ( + "github.com/ethereum/go-ethereum/common" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/vulcanize/vulcanizedb/examples/erc20_watcher/event_triggered" + "github.com/vulcanize/vulcanizedb/examples/test_helpers" + "github.com/vulcanize/vulcanizedb/pkg/config" + "github.com/vulcanize/vulcanizedb/pkg/core" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" + "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories" + "math/rand" + "time" +) + +var transferEntity = event_triggered.TransferEntity{ + TokenName: "Dai", + TokenAddress: common.HexToAddress("0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"), + Src: common.HexToAddress("0x000000000000000000000000000000000000Af21"), + Dst: common.HexToAddress("0x09BbBBE21a5975cAc061D82f7b843bCE061BA391"), + Wad: &transferWad, + Block: 5488076, + TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad6546ae", +} + +var approvalEntity = event_triggered.ApprovalEntity{ + TokenName: "Dai", + TokenAddress: common.HexToAddress("0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"), + Src: common.HexToAddress("0x000000000000000000000000000000000000Af21"), + Guy: common.HexToAddress("0x09BbBBE21a5975cAc061D82f7b843bCE061BA391"), + Wad: &approvalWad, + Block: 5488076, + TxHash: "0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad6546ae", +} + +var _ = Describe("Approval and Transfer Repository Tests", func() { + var db *postgres.DB + var repository event_triggered.Repository + var logRepository repositories.LogRepository + var blockRepository repositories.BlockRepository + var receiptRepository repositories.ReceiptRepository + var blockNumber int64 + var blockId int64 + var vulcanizeLogId int64 + rand.Seed(time.Now().UnixNano()) + + BeforeEach(func() { + var err error + db, err = postgres.NewDB(config.Database{ + Hostname: "localhost", + Name: "vulcanize_private", + Port: 5432, + }, core.Node{}) + Expect(err).NotTo(HaveOccurred()) + + receiptRepository = repositories.ReceiptRepository{DB: db} + logRepository = repositories.LogRepository{DB: db} + blockRepository = *repositories.NewBlockRepository(db) + + blockNumber = rand.Int63() + blockId = test_helpers.CreateBlock(blockNumber, blockRepository) + + log := core.Log{} + logs := []core.Log{log} + receipt := core.Receipt{ + Logs: logs, + } + receipts := []core.Receipt{receipt} + + err = receiptRepository.CreateReceiptsAndLogs(blockId, receipts) + Expect(err).ToNot(HaveOccurred()) + + err = logRepository.Get(&vulcanizeLogId, `SELECT id FROM logs`) + Expect(err).ToNot(HaveOccurred()) + + repository = event_triggered.Repository{DB: db} + + }) + + AfterEach(func() { + db.Query(`DELETE FROM logs`) + db.Query(`DELETE FROM log_filters`) + db.Query(`DELETE FROM token_transfers`) + + repository.DB.Exec(`DELETE FROM token_transfers`) + }) + + It("Creates a new Transfer record", func() { + converter := event_triggered.ERC20Converter{} + model := converter.ToTransferModel(transferEntity) + err := repository.CreateTransfer(model, vulcanizeLogId) + Expect(err).ToNot(HaveOccurred()) + type DBRow struct { + DBID uint64 `db:"id"` + VulcanizeLogID int64 `db:"vulcanize_log_id"` + event_triggered.TransferModel + } + dbResult := DBRow{} + + err = repository.QueryRowx(`SELECT * FROM token_transfers`).StructScan(&dbResult) + Expect(err).ToNot(HaveOccurred()) + + Expect(dbResult.VulcanizeLogID).To(Equal(vulcanizeLogId)) + Expect(dbResult.TokenName).To(Equal(model.TokenName)) + Expect(dbResult.TokenAddress).To(Equal(model.TokenAddress)) + Expect(dbResult.To).To(Equal(model.To)) + Expect(dbResult.From).To(Equal(model.From)) + Expect(dbResult.Tokens).To(Equal(model.Tokens)) + Expect(dbResult.Block).To(Equal(model.Block)) + Expect(dbResult.TxHash).To(Equal(model.TxHash)) + }) + + It("does not duplicate token_transfers that have already been seen", func() { + converter := event_triggered.ERC20Converter{} + model := converter.ToTransferModel(transferEntity) + + err := repository.CreateTransfer(model, vulcanizeLogId) + Expect(err).ToNot(HaveOccurred()) + err = repository.CreateTransfer(model, vulcanizeLogId) + Expect(err).ToNot(HaveOccurred()) + + var count int + err = repository.DB.QueryRowx(`SELECT count(*) FROM token_transfers`).Scan(&count) + Expect(err).ToNot(HaveOccurred()) + Expect(count).To(Equal(1)) + }) + + It("Removes a Transfer record when the corresponding log is removed", func() { + var exists bool + + converter := event_triggered.ERC20Converter{} + model := converter.ToTransferModel(transferEntity) + err := repository.CreateTransfer(model, vulcanizeLogId) + Expect(err).ToNot(HaveOccurred()) + + err = repository.DB.QueryRow(`SELECT exists (SELECT * FROM token_transfers WHERE vulcanize_log_id = $1)`, vulcanizeLogId).Scan(&exists) + Expect(err).ToNot(HaveOccurred()) + Expect(exists).To(BeTrue()) + + var logCount int + _, err = logRepository.DB.Exec(`DELETE FROM logs WHERE id = $1`, vulcanizeLogId) + Expect(err).ToNot(HaveOccurred()) + err = logRepository.Get(&logCount, `SELECT count(*) FROM logs WHERE id = $1`, vulcanizeLogId) + Expect(err).ToNot(HaveOccurred()) + Expect(logCount).To(BeZero()) + + var LogKillCount int + err = repository.DB.QueryRowx( + `SELECT count(*) FROM token_transfers WHERE vulcanize_log_id = $1`, vulcanizeLogId).Scan(&LogKillCount) + Expect(err).ToNot(HaveOccurred()) + Expect(LogKillCount).To(BeZero()) + }) + + It("Creates a new Approval record", func() { + converter := event_triggered.ERC20Converter{} + model := converter.ToApprovalModel(approvalEntity) + err := repository.CreateApproval(model, vulcanizeLogId) + Expect(err).ToNot(HaveOccurred()) + type DBRow struct { + DBID uint64 `db:"id"` + VulcanizeLogID int64 `db:"vulcanize_log_id"` + event_triggered.ApprovalModel + } + dbResult := DBRow{} + + err = repository.QueryRowx(`SELECT * FROM token_approvals`).StructScan(&dbResult) + Expect(err).ToNot(HaveOccurred()) + + Expect(dbResult.VulcanizeLogID).To(Equal(vulcanizeLogId)) + Expect(dbResult.TokenName).To(Equal(model.TokenName)) + Expect(dbResult.TokenAddress).To(Equal(model.TokenAddress)) + Expect(dbResult.Owner).To(Equal(model.Owner)) + Expect(dbResult.Spender).To(Equal(model.Spender)) + Expect(dbResult.Tokens).To(Equal(model.Tokens)) + Expect(dbResult.Block).To(Equal(model.Block)) + Expect(dbResult.TxHash).To(Equal(model.TxHash)) + }) + + It("does not duplicate token_approvals that have already been seen", func() { + converter := event_triggered.ERC20Converter{} + model := converter.ToApprovalModel(approvalEntity) + + err := repository.CreateApproval(model, vulcanizeLogId) + Expect(err).ToNot(HaveOccurred()) + err = repository.CreateApproval(model, vulcanizeLogId) + Expect(err).ToNot(HaveOccurred()) + + var count int + err = repository.DB.QueryRowx(`SELECT count(*) FROM token_approvals`).Scan(&count) + Expect(err).ToNot(HaveOccurred()) + Expect(count).To(Equal(1)) + }) + + It("Removes a Approval record when the corresponding log is removed", func() { + var exists bool + + converter := event_triggered.ERC20Converter{} + model := converter.ToApprovalModel(approvalEntity) + err := repository.CreateApproval(model, vulcanizeLogId) + Expect(err).ToNot(HaveOccurred()) + + err = repository.DB.QueryRow(`SELECT exists (SELECT * FROM token_approvals WHERE vulcanize_log_id = $1)`, vulcanizeLogId).Scan(&exists) + Expect(err).ToNot(HaveOccurred()) + Expect(exists).To(BeTrue()) + + var logCount int + _, err = logRepository.DB.Exec(`DELETE FROM logs WHERE id = $1`, vulcanizeLogId) + Expect(err).ToNot(HaveOccurred()) + err = logRepository.Get(&logCount, `SELECT count(*) FROM logs WHERE id = $1`, vulcanizeLogId) + Expect(err).ToNot(HaveOccurred()) + Expect(logCount).To(BeZero()) + + var LogKillCount int + err = repository.DB.QueryRowx( + `SELECT count(*) FROM token_approvals WHERE vulcanize_log_id = $1`, vulcanizeLogId).Scan(&LogKillCount) + Expect(err).ToNot(HaveOccurred()) + Expect(LogKillCount).To(BeZero()) + }) +})