(VDB-298) Consume Pit contract storage diffs

- Continuously parse storage diffs CSV data to read Pit contract state
- Convert ilks in database to raw bytes32 value for use in generating
  storage keys dynamically
- Persist storage diffs with block number and hash for validation
This commit is contained in:
Rob Mulholand
2019-02-06 10:31:46 -06:00
parent c474933432
commit 867f92c431
85 changed files with 2143 additions and 79 deletions
@@ -0,0 +1,146 @@
// VulcanizeDB
// Copyright © 2018 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package pit
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs"
"github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/maker"
"github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared"
"math/big"
)
const (
IlkLine = "line"
IlkSpot = "spot"
PitDrip = "drip"
PitLine = "Line"
PitLive = "live"
PitVat = "vat"
)
var (
// storage key and value metadata for "drip" on the Pit contract
DripKey = common.HexToHash(storage_diffs.IndexFive)
DripMetadata = shared.StorageValueMetadata{
Name: PitDrip,
Key: "",
Type: shared.Address,
}
IlkSpotIndex = storage_diffs.IndexOne
// storage key and value metadata for "Spot" on the Pit contract
LineKey = common.HexToHash(storage_diffs.IndexThree)
LineMetadata = shared.StorageValueMetadata{
Name: PitLine,
Key: "",
Type: shared.Uint256,
}
// storage key and value metadata for "live" on the Pit contract
LiveKey = common.HexToHash(storage_diffs.IndexTwo)
LiveMetadata = shared.StorageValueMetadata{
Name: PitLive,
Key: "",
Type: shared.Uint256,
}
// storage key and value metadata for "vat" on the Pit contract
VatKey = common.HexToHash(storage_diffs.IndexFour)
VatMetadata = shared.StorageValueMetadata{
Name: PitVat,
Key: "",
Type: shared.Address,
}
)
type PitMappings struct {
StorageRepository maker.IMakerStorageRepository
mappings map[common.Hash]shared.StorageValueMetadata
}
func (mappings *PitMappings) SetDB(db *postgres.DB) {
mappings.StorageRepository.SetDB(db)
}
func (mappings *PitMappings) Lookup(key common.Hash) (shared.StorageValueMetadata, error) {
metadata, ok := mappings.mappings[key]
if !ok {
err := mappings.loadMappings()
if err != nil {
return metadata, err
}
metadata, ok = mappings.mappings[key]
if !ok {
return metadata, shared.ErrStorageKeyNotFound{Key: key.Hex()}
}
}
return metadata, nil
}
func (mappings *PitMappings) loadMappings() error {
mappings.mappings = getStaticMappings()
ilks, err := mappings.StorageRepository.GetIlks()
if err != nil {
return err
}
for _, ilk := range ilks {
mappings.mappings[getSpotKey(ilk)] = getSpotMetadata(ilk)
mappings.mappings[getLineKey(ilk)] = getLineMetadata(ilk)
}
return nil
}
func getStaticMappings() map[common.Hash]shared.StorageValueMetadata {
mappings := make(map[common.Hash]shared.StorageValueMetadata)
mappings[DripKey] = DripMetadata
mappings[LineKey] = LineMetadata
mappings[LiveKey] = LiveMetadata
mappings[VatKey] = VatMetadata
return mappings
}
func getSpotKey(ilk string) common.Hash {
keyBytes := common.FromHex("0x" + ilk + IlkSpotIndex)
encoded := crypto.Keccak256(keyBytes)
return common.BytesToHash(encoded)
}
func getSpotMetadata(ilk string) shared.StorageValueMetadata {
return shared.StorageValueMetadata{
Name: IlkSpot,
Key: ilk,
Type: shared.Uint256,
}
}
func getLineKey(ilk string) common.Hash {
spotMappingAsInt := big.NewInt(0).SetBytes(getSpotKey(ilk).Bytes())
incrementedByOne := big.NewInt(0).Add(spotMappingAsInt, big.NewInt(1))
return common.BytesToHash(incrementedByOne.Bytes())
}
func getLineMetadata(ilk string) shared.StorageValueMetadata {
return shared.StorageValueMetadata{
Name: IlkLine,
Key: ilk,
Type: shared.Uint256,
}
}
@@ -0,0 +1,90 @@
package pit_test
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/fakes"
"github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/maker/pit"
"github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/maker/test_helpers"
"github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared"
"math/big"
)
var _ = Describe("Pit storage mappings", func() {
Describe("looking up static keys", func() {
It("returns value metadata if key exists", func() {
storageRepository := &test_helpers.MockMakerStorageRepository{}
mappings := pit.PitMappings{StorageRepository: storageRepository}
Expect(mappings.Lookup(pit.DripKey)).To(Equal(pit.DripMetadata))
Expect(mappings.Lookup(pit.LineKey)).To(Equal(pit.LineMetadata))
Expect(mappings.Lookup(pit.LiveKey)).To(Equal(pit.LiveMetadata))
Expect(mappings.Lookup(pit.VatKey)).To(Equal(pit.VatMetadata))
})
It("returns error if key does not exist", func() {
mappings := pit.PitMappings{StorageRepository: &test_helpers.MockMakerStorageRepository{}}
_, err := mappings.Lookup(common.HexToHash(fakes.FakeHash.Hex()))
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(shared.ErrStorageKeyNotFound{Key: fakes.FakeHash.Hex()}))
})
})
Describe("looking up dynamic keys", func() {
It("refreshes mappings from repository if key not found", func() {
storageRepository := &test_helpers.MockMakerStorageRepository{}
mappings := pit.PitMappings{StorageRepository: storageRepository}
mappings.Lookup(fakes.FakeHash)
Expect(storageRepository.GetIlksCalled).To(BeTrue())
})
It("returns value metadata for spot when ilk in the DB", func() {
storageRepository := &test_helpers.MockMakerStorageRepository{}
fakeIlk := "fakeIlk"
storageRepository.SetIlks([]string{fakeIlk})
mappings := pit.PitMappings{StorageRepository: storageRepository}
ilkSpotKey := common.BytesToHash(crypto.Keccak256(common.FromHex("0x" + fakeIlk + pit.IlkSpotIndex)))
expectedMetadata := shared.StorageValueMetadata{
Name: pit.IlkSpot,
Key: fakeIlk,
Type: shared.Uint256,
}
Expect(mappings.Lookup(ilkSpotKey)).To(Equal(expectedMetadata))
})
It("returns value metadata for line when ilk in the DB", func() {
storageRepository := &test_helpers.MockMakerStorageRepository{}
fakeIlk := "fakeIlk"
storageRepository.SetIlks([]string{fakeIlk})
mappings := pit.PitMappings{StorageRepository: storageRepository}
ilkSpotKeyBytes := crypto.Keccak256(common.FromHex("0x" + fakeIlk + pit.IlkSpotIndex))
ilkSpotAsInt := big.NewInt(0).SetBytes(ilkSpotKeyBytes)
incrementedIlkSpot := big.NewInt(0).Add(ilkSpotAsInt, big.NewInt(1))
ilkLineKey := common.BytesToHash(incrementedIlkSpot.Bytes())
expectedMetadata := shared.StorageValueMetadata{
Name: pit.IlkLine,
Key: fakeIlk,
Type: shared.Uint256,
}
Expect(mappings.Lookup(ilkLineKey)).To(Equal(expectedMetadata))
})
It("returns error if key not found", func() {
storageRepository := &test_helpers.MockMakerStorageRepository{}
mappings := pit.PitMappings{StorageRepository: storageRepository}
_, err := mappings.Lookup(fakes.FakeHash)
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(shared.ErrStorageKeyNotFound{Key: fakes.FakeHash.Hex()}))
})
})
})
@@ -0,0 +1,13 @@
package pit_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestPit(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Pit Suite")
}
@@ -0,0 +1,79 @@
// VulcanizeDB
// Copyright © 2018 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package pit
import (
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared"
)
type PitStorageRepository struct {
db *postgres.DB
}
func (repository *PitStorageRepository) SetDB(db *postgres.DB) {
repository.db = db
}
func (repository PitStorageRepository) Create(blockNumber int, blockHash string, metadata shared.StorageValueMetadata, value interface{}) error {
switch metadata.Name {
case IlkLine:
return repository.insertIlkLine(blockNumber, blockHash, metadata.Key, value.(string))
case IlkSpot:
return repository.insertIlkSpot(blockNumber, blockHash, metadata.Key, value.(string))
case PitDrip:
return repository.insertPitDrip(blockNumber, blockHash, value.(string))
case PitLine:
return repository.insertPitLine(blockNumber, blockHash, value.(string))
case PitLive:
return repository.insertPitLive(blockNumber, blockHash, value.(string))
case PitVat:
return repository.insertPitVat(blockNumber, blockHash, value.(string))
default:
panic("unrecognized storage metadata name")
}
}
func (repository PitStorageRepository) insertIlkLine(blockNumber int, blockHash string, ilk string, line string) error {
_, err := repository.db.Exec(`INSERT INTO maker.pit_ilk_line (block_number, block_hash, ilk, line) VALUES ($1, $2, $3, $4)`, blockNumber, blockHash, ilk, line)
return err
}
func (repository PitStorageRepository) insertIlkSpot(blockNumber int, blockHash string, ilk string, spot string) error {
_, err := repository.db.Exec(`INSERT INTO maker.pit_ilk_spot (block_number, block_hash, ilk, spot) VALUES ($1, $2, $3, $4)`, blockNumber, blockHash, ilk, spot)
return err
}
func (repository PitStorageRepository) insertPitDrip(blockNumber int, blockHash string, drip string) error {
_, err := repository.db.Exec(`INSERT INTO maker.pit_drip (block_number, block_hash, drip) VALUES ($1, $2, $3)`, blockNumber, blockHash, drip)
return err
}
func (repository PitStorageRepository) insertPitLine(blockNumber int, blockHash string, line string) error {
_, err := repository.db.Exec(`INSERT INTO maker.pit_line (block_number, block_hash, line) VALUES ($1, $2, $3)`, blockNumber, blockHash, line)
return err
}
func (repository PitStorageRepository) insertPitLive(blockNumber int, blockHash string, live string) error {
_, err := repository.db.Exec(`INSERT INTO maker.pit_live (block_number, block_hash, live) VALUES ($1, $2, $3)`, blockNumber, blockHash, live)
return err
}
func (repository PitStorageRepository) insertPitVat(blockNumber int, blockHash string, vat string) error {
_, err := repository.db.Exec(`INSERT INTO maker.pit_vat (block_number, block_hash, vat) VALUES ($1, $2, $3)`, blockNumber, blockHash, vat)
return err
}
@@ -0,0 +1,172 @@
// VulcanizeDB
// Copyright © 2018 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package pit_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
"github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/maker/pit"
"github.com/vulcanize/vulcanizedb/pkg/transformers/storage_diffs/shared"
"github.com/vulcanize/vulcanizedb/test_config"
)
var _ = Describe("Pit storage repository", func() {
var (
blockNumber int
blockHash string
db *postgres.DB
err error
repo pit.PitStorageRepository
)
BeforeEach(func() {
blockNumber = 123
blockHash = "expected_block_hash"
db = test_config.NewTestDB(test_config.NewTestNode())
test_config.CleanTestDB(db)
repo = pit.PitStorageRepository{}
repo.SetDB(db)
})
It("persists an ilk line", func() {
expectedIlk := "fake_ilk"
expectedLine := "12345"
ilkLineMetadata := shared.StorageValueMetadata{
Name: pit.IlkLine,
Key: expectedIlk,
Type: shared.Uint256,
}
err = repo.Create(blockNumber, blockHash, ilkLineMetadata, expectedLine)
Expect(err).NotTo(HaveOccurred())
type IlkLine struct {
BlockMetadata
Ilk string
Line string
}
var result IlkLine
err = db.Get(&result, `SELECT block_number, block_hash, ilk, line FROM maker.pit_ilk_line`)
Expect(err).NotTo(HaveOccurred())
Expect(result.BlockNumber).To(Equal(blockNumber))
Expect(result.BlockHash).To(Equal(blockHash))
Expect(result.Ilk).To(Equal(expectedIlk))
Expect(result.Line).To(Equal(expectedLine))
})
It("persists an ilk spot", func() {
expectedIlk := "fake_ilk"
expectedSpot := "12345"
ilkSpotMetadata := shared.StorageValueMetadata{
Name: pit.IlkSpot,
Key: expectedIlk,
Type: shared.Uint256,
}
err = repo.Create(blockNumber, blockHash, ilkSpotMetadata, expectedSpot)
Expect(err).NotTo(HaveOccurred())
type IlkSpot struct {
BlockMetadata
Ilk string
Spot string
}
var result IlkSpot
err = db.Get(&result, `SELECT block_number, block_hash, ilk, spot FROM maker.pit_ilk_spot`)
Expect(err).NotTo(HaveOccurred())
Expect(result.BlockNumber).To(Equal(blockNumber))
Expect(result.BlockHash).To(Equal(blockHash))
Expect(result.Ilk).To(Equal(expectedIlk))
Expect(result.Spot).To(Equal(expectedSpot))
})
It("persists a pit drip", func() {
expectedDrip := "0x0123456789abcdef0123"
err = repo.Create(blockNumber, blockHash, pit.DripMetadata, expectedDrip)
Expect(err).NotTo(HaveOccurred())
type PitDrip struct {
BlockMetadata
Drip string
}
var result PitDrip
err = db.Get(&result, `SELECT block_number, block_hash, drip FROM maker.pit_drip`)
Expect(err).NotTo(HaveOccurred())
Expect(result.BlockNumber).To(Equal(blockNumber))
Expect(result.BlockHash).To(Equal(blockHash))
Expect(result.Drip).To(Equal(expectedDrip))
})
It("persists a pit line", func() {
expectedLine := "12345"
err = repo.Create(blockNumber, blockHash, pit.LineMetadata, expectedLine)
Expect(err).NotTo(HaveOccurred())
type PitLine struct {
BlockMetadata
Line string
}
var result PitLine
err = db.Get(&result, `SELECT block_number, block_hash, line FROM maker.pit_line`)
Expect(err).NotTo(HaveOccurred())
Expect(result.BlockNumber).To(Equal(blockNumber))
Expect(result.BlockHash).To(Equal(blockHash))
Expect(result.Line).To(Equal(expectedLine))
})
It("persists a pit live", func() {
expectedLive := "12345"
err = repo.Create(blockNumber, blockHash, pit.LiveMetadata, expectedLive)
Expect(err).NotTo(HaveOccurred())
type PitLive struct {
BlockMetadata
Live string
}
var result PitLive
err = db.Get(&result, `SELECT block_number, block_hash, live FROM maker.pit_live`)
Expect(err).NotTo(HaveOccurred())
Expect(result.BlockNumber).To(Equal(blockNumber))
Expect(result.BlockHash).To(Equal(blockHash))
Expect(result.Live).To(Equal(expectedLive))
})
It("persists a pit vat", func() {
expectedVat := "0x0123456789abcdef0123"
err = repo.Create(blockNumber, blockHash, pit.VatMetadata, expectedVat)
Expect(err).NotTo(HaveOccurred())
type PitVat struct {
BlockMetadata
Vat string
}
var result PitVat
err = db.Get(&result, `SELECT block_number, block_hash, vat FROM maker.pit_vat`)
Expect(err).NotTo(HaveOccurred())
Expect(result.BlockNumber).To(Equal(blockNumber))
Expect(result.BlockHash).To(Equal(blockHash))
Expect(result.Vat).To(Equal(expectedVat))
})
})
type BlockMetadata struct {
BlockNumber int `db:"block_number"`
BlockHash string `db:"block_hash"`
}