diff --git a/libraries/shared/factories/storage/README.md b/libraries/shared/factories/storage/README.md
index e22eb31b2..f79e8987b 100644
--- a/libraries/shared/factories/storage/README.md
+++ b/libraries/shared/factories/storage/README.md
@@ -31,15 +31,15 @@ The storage transformer depends on contract-specific implementations of code cap
```golang
func (transformer Transformer) Execute(row shared.StorageDiffRow) error {
- metadata, lookupErr := transformer.Mappings.Lookup(row.StorageKey)
+ metadata, lookupErr := transformer.StorageKeysLookup.Lookup(diff.StorageKey)
if lookupErr != nil {
return lookupErr
}
- value, decodeErr := shared.Decode(row, metadata)
+ value, decodeErr := utils.Decode(diff, metadata)
if decodeErr != nil {
return decodeErr
}
- return transformer.Repository.Create(row.BlockHeight, row.BlockHash.Hex(), metadata, value)
+ return transformer.Repository.Create(diff.BlockHeight, diff.BlockHash.Hex(), metadata, value)
}
```
@@ -47,20 +47,36 @@ func (transformer Transformer) Execute(row shared.StorageDiffRow) error {
In order to watch an additional smart contract, a developer must create three things:
-1. Mappings - specify how to identify keys in the contract's storage trie.
+1. StorageKeysLoader - identify keys in the contract's storage trie, providing metadata to describe how associated values should be decoded.
1. Repository - specify how to persist a parsed version of the storage value matching the recognized storage key.
1. Instance - create an instance of the storage transformer that uses your mappings and repository.
-### Mappings
+### StorageKeysLoader
+
+A `StorageKeysLoader` is used by the `StorageKeysLookup` object on a storage transformer.
```golang
-type Mappings interface {
- Lookup(key common.Hash) (shared.StorageValueMetadata, error)
+type KeysLoader interface {
+ LoadMappings() (map[common.Hash]utils.StorageValueMetadata, error)
SetDB(db *postgres.DB)
}
```
-A contract-specific implementation of the mappings interface enables the storage transformer to fetch metadata associated with a storage key.
+When a key is not found, the lookup object refreshes its known keys by calling the loader.
+
+```golang
+func (lookup *keysLookup) refreshMappings() error {
+ var err error
+ lookup.mappings, err = lookup.loader.LoadMappings()
+ if err != nil {
+ return err
+ }
+ lookup.mappings = utils.AddHashedKeys(lookup.mappings)
+ return nil
+}
+```
+
+A contract-specific implementation of the loader enables the storage transformer to fetch metadata associated with a storage key.
Storage metadata contains: the name of the variable matching the storage key, a raw version of any keys associated with the variable (if the variable is a mapping), and the variable's type.
@@ -72,7 +88,7 @@ type StorageValueMetadata struct {
}
```
-Keys are only relevant if the variable is a mapping. For example, in the following Solidity code:
+The `Keys` field on the metadata is only relevant if the variable is a mapping. For example, in the following Solidity code:
```solidity
pragma solidity ^0.4.0;
@@ -85,7 +101,7 @@ contract Contract {
The metadata for variable `x` would not have any associated keys, but the metadata for a storage key associated with `y` would include the address used to specify that key's index in the mapping.
-The `SetDB` function is required for the mappings to connect to the database.
+The `SetDB` function is required for the storage key loader to connect to the database.
A database connection may be desired when keys in a mapping variable need to be read from log events (e.g. to lookup what addresses may exist in `y`, above).
### Repository
diff --git a/libraries/shared/factories/storage/keys_loader.go b/libraries/shared/factories/storage/keys_loader.go
new file mode 100644
index 000000000..1b6efdbbc
--- /dev/null
+++ b/libraries/shared/factories/storage/keys_loader.go
@@ -0,0 +1,28 @@
+// VulcanizeDB
+// Copyright © 2019 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 .
+
+package storage
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
+ "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
+)
+
+type KeysLoader interface {
+ LoadMappings() (map[common.Hash]utils.StorageValueMetadata, error)
+ SetDB(db *postgres.DB)
+}
diff --git a/libraries/shared/factories/storage/keys_lookup.go b/libraries/shared/factories/storage/keys_lookup.go
new file mode 100644
index 000000000..1c641c200
--- /dev/null
+++ b/libraries/shared/factories/storage/keys_lookup.go
@@ -0,0 +1,66 @@
+// VulcanizeDB
+// Copyright © 2019 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 .
+
+package storage
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
+ "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
+)
+
+type KeysLookup interface {
+ Lookup(key common.Hash) (utils.StorageValueMetadata, error)
+ SetDB(db *postgres.DB)
+}
+
+type keysLookup struct {
+ loader KeysLoader
+ mappings map[common.Hash]utils.StorageValueMetadata
+}
+
+func NewKeysLookup(loader KeysLoader) KeysLookup {
+ return &keysLookup{loader: loader, mappings: make(map[common.Hash]utils.StorageValueMetadata)}
+}
+
+func (lookup *keysLookup) Lookup(key common.Hash) (utils.StorageValueMetadata, error) {
+ metadata, ok := lookup.mappings[key]
+ if !ok {
+ refreshErr := lookup.refreshMappings()
+ if refreshErr != nil {
+ return metadata, refreshErr
+ }
+ metadata, ok = lookup.mappings[key]
+ if !ok {
+ return metadata, utils.ErrStorageKeyNotFound{Key: key.Hex()}
+ }
+ }
+ return metadata, nil
+}
+
+func (lookup *keysLookup) refreshMappings() error {
+ var err error
+ lookup.mappings, err = lookup.loader.LoadMappings()
+ if err != nil {
+ return err
+ }
+ lookup.mappings = utils.AddHashedKeys(lookup.mappings)
+ return nil
+}
+
+func (lookup *keysLookup) SetDB(db *postgres.DB) {
+ lookup.loader.SetDB(db)
+}
diff --git a/libraries/shared/factories/storage/keys_lookup_test.go b/libraries/shared/factories/storage/keys_lookup_test.go
new file mode 100644
index 000000000..6a77b1644
--- /dev/null
+++ b/libraries/shared/factories/storage/keys_lookup_test.go
@@ -0,0 +1,113 @@
+// VulcanizeDB
+// Copyright © 2019 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 .
+
+package storage_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/libraries/shared/factories/storage"
+ "github.com/vulcanize/vulcanizedb/libraries/shared/mocks"
+ "github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
+ "github.com/vulcanize/vulcanizedb/pkg/fakes"
+ "github.com/vulcanize/vulcanizedb/test_config"
+)
+
+var _ = Describe("Storage keys lookup", func() {
+ var (
+ fakeMetadata = utils.GetStorageValueMetadata("name", map[utils.Key]string{}, utils.Uint256)
+ lookup storage.KeysLookup
+ loader *mocks.MockStorageKeysLoader
+ )
+
+ BeforeEach(func() {
+ loader = &mocks.MockStorageKeysLoader{}
+ lookup = storage.NewKeysLookup(loader)
+ })
+
+ Describe("Lookup", func() {
+ Describe("when key not found", func() {
+ It("refreshes keys", func() {
+ loader.StorageKeyMappings = map[common.Hash]utils.StorageValueMetadata{fakes.FakeHash: fakeMetadata}
+ _, err := lookup.Lookup(fakes.FakeHash)
+
+ Expect(err).NotTo(HaveOccurred())
+ Expect(loader.LoadMappingsCallCount).To(Equal(1))
+ })
+
+ It("returns error if refreshing keys fails", func() {
+ loader.LoadMappingsError = fakes.FakeError
+
+ _, err := lookup.Lookup(fakes.FakeHash)
+
+ Expect(err).To(HaveOccurred())
+ Expect(err).To(MatchError(fakes.FakeError))
+ })
+ })
+
+ Describe("when key found", func() {
+ BeforeEach(func() {
+ loader.StorageKeyMappings = map[common.Hash]utils.StorageValueMetadata{fakes.FakeHash: fakeMetadata}
+ _, err := lookup.Lookup(fakes.FakeHash)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(loader.LoadMappingsCallCount).To(Equal(1))
+ })
+
+ It("does not refresh keys", func() {
+ _, err := lookup.Lookup(fakes.FakeHash)
+
+ Expect(err).NotTo(HaveOccurred())
+ Expect(loader.LoadMappingsCallCount).To(Equal(1))
+ })
+ })
+
+ It("returns metadata for loaded static key", func() {
+ loader.StorageKeyMappings = map[common.Hash]utils.StorageValueMetadata{fakes.FakeHash: fakeMetadata}
+
+ metadata, err := lookup.Lookup(fakes.FakeHash)
+
+ Expect(err).NotTo(HaveOccurred())
+ Expect(metadata).To(Equal(fakeMetadata))
+ })
+
+ It("returns metadata for hashed version of key (accommodates keys emitted from Geth)", func() {
+ loader.StorageKeyMappings = map[common.Hash]utils.StorageValueMetadata{fakes.FakeHash: fakeMetadata}
+
+ hashedKey := common.BytesToHash(crypto.Keccak256(fakes.FakeHash.Bytes()))
+ metadata, err := lookup.Lookup(hashedKey)
+
+ Expect(err).NotTo(HaveOccurred())
+ Expect(metadata).To(Equal(fakeMetadata))
+ })
+
+ It("returns key not found error if key not found", func() {
+ _, err := lookup.Lookup(fakes.FakeHash)
+
+ Expect(err).To(HaveOccurred())
+ Expect(err).To(MatchError(utils.ErrStorageKeyNotFound{Key: fakes.FakeHash.Hex()}))
+ })
+ })
+
+ Describe("SetDB", func() {
+ It("sets the db on the loader", func() {
+ lookup.SetDB(test_config.NewTestDB(test_config.NewTestNode()))
+
+ Expect(loader.SetDBCalled).To(BeTrue())
+ })
+ })
+})
diff --git a/libraries/shared/factories/storage/transformer.go b/libraries/shared/factories/storage/transformer.go
index b38e5760d..25e39811c 100644
--- a/libraries/shared/factories/storage/transformer.go
+++ b/libraries/shared/factories/storage/transformer.go
@@ -18,20 +18,19 @@ package storage
import (
"github.com/ethereum/go-ethereum/common"
- "github.com/vulcanize/vulcanizedb/libraries/shared/storage"
"github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
)
type Transformer struct {
- HashedAddress common.Hash
- Mappings storage.Mappings
- Repository Repository
+ HashedAddress common.Hash
+ StorageKeysLookup KeysLookup
+ Repository Repository
}
func (transformer Transformer) NewTransformer(db *postgres.DB) transformer.StorageTransformer {
- transformer.Mappings.SetDB(db)
+ transformer.StorageKeysLookup.SetDB(db)
transformer.Repository.SetDB(db)
return transformer
}
@@ -41,7 +40,7 @@ func (transformer Transformer) KeccakContractAddress() common.Hash {
}
func (transformer Transformer) Execute(diff utils.StorageDiff) error {
- metadata, lookupErr := transformer.Mappings.Lookup(diff.StorageKey)
+ metadata, lookupErr := transformer.StorageKeysLookup.Lookup(diff.StorageKey)
if lookupErr != nil {
return lookupErr
}
diff --git a/libraries/shared/factories/storage/transformer_test.go b/libraries/shared/factories/storage/transformer_test.go
index 8fe87e524..fddb082fd 100644
--- a/libraries/shared/factories/storage/transformer_test.go
+++ b/libraries/shared/factories/storage/transformer_test.go
@@ -28,18 +28,18 @@ import (
var _ = Describe("Storage transformer", func() {
var (
- mappings *mocks.MockMappings
- repository *mocks.MockStorageRepository
- t storage.Transformer
+ storageKeysLookup *mocks.MockStorageKeysLookup
+ repository *mocks.MockStorageRepository
+ t storage.Transformer
)
BeforeEach(func() {
- mappings = &mocks.MockMappings{}
+ storageKeysLookup = &mocks.MockStorageKeysLookup{}
repository = &mocks.MockStorageRepository{}
t = storage.Transformer{
- HashedAddress: common.Hash{},
- Mappings: mappings,
- Repository: repository,
+ HashedAddress: common.Hash{},
+ StorageKeysLookup: storageKeysLookup,
+ Repository: repository,
}
})
@@ -53,11 +53,11 @@ var _ = Describe("Storage transformer", func() {
It("looks up metadata for storage key", func() {
t.Execute(utils.StorageDiff{})
- Expect(mappings.LookupCalled).To(BeTrue())
+ Expect(storageKeysLookup.LookupCalled).To(BeTrue())
})
It("returns error if lookup fails", func() {
- mappings.LookupErr = fakes.FakeError
+ storageKeysLookup.LookupErr = fakes.FakeError
err := t.Execute(utils.StorageDiff{})
@@ -67,7 +67,7 @@ var _ = Describe("Storage transformer", func() {
It("creates storage row with decoded data", func() {
fakeMetadata := utils.StorageValueMetadata{Type: utils.Address}
- mappings.Metadata = fakeMetadata
+ storageKeysLookup.Metadata = fakeMetadata
rawValue := common.HexToAddress("0x12345")
fakeBlockNumber := 123
fakeBlockHash := "0x67890"
@@ -91,7 +91,7 @@ var _ = Describe("Storage transformer", func() {
It("returns error if creating row fails", func() {
rawValue := common.HexToAddress("0x12345")
fakeMetadata := utils.StorageValueMetadata{Type: utils.Address}
- mappings.Metadata = fakeMetadata
+ storageKeysLookup.Metadata = fakeMetadata
repository.CreateErr = fakes.FakeError
err := t.Execute(utils.StorageDiff{StorageValue: rawValue.Hash()})
@@ -118,7 +118,7 @@ var _ = Describe("Storage transformer", func() {
}
It("passes the decoded data items to the repository", func() {
- mappings.Metadata = fakeMetadata
+ storageKeysLookup.Metadata = fakeMetadata
fakeRow := utils.StorageDiff{
HashedAddress: common.Hash{},
BlockHash: common.HexToHash(fakeBlockHash),
@@ -140,7 +140,7 @@ var _ = Describe("Storage transformer", func() {
})
It("returns error if creating a row fails", func() {
- mappings.Metadata = fakeMetadata
+ storageKeysLookup.Metadata = fakeMetadata
repository.CreateErr = fakes.FakeError
err := t.Execute(utils.StorageDiff{StorageValue: rawValue.Hash()})
diff --git a/libraries/shared/mocks/storage_keys_loader.go b/libraries/shared/mocks/storage_keys_loader.go
new file mode 100644
index 000000000..4b2c08122
--- /dev/null
+++ b/libraries/shared/mocks/storage_keys_loader.go
@@ -0,0 +1,39 @@
+// VulcanizeDB
+// Copyright © 2019 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 .
+
+package mocks
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
+ "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
+)
+
+type MockStorageKeysLoader struct {
+ LoadMappingsCallCount int
+ LoadMappingsError error
+ SetDBCalled bool
+ StorageKeyMappings map[common.Hash]utils.StorageValueMetadata
+}
+
+func (loader *MockStorageKeysLoader) LoadMappings() (map[common.Hash]utils.StorageValueMetadata, error) {
+ loader.LoadMappingsCallCount++
+ return loader.StorageKeyMappings, loader.LoadMappingsError
+}
+
+func (loader *MockStorageKeysLoader) SetDB(db *postgres.DB) {
+ loader.SetDBCalled = true
+}
diff --git a/libraries/shared/mocks/storage_mappings.go b/libraries/shared/mocks/storage_keys_lookup.go
similarity index 85%
rename from libraries/shared/mocks/storage_mappings.go
rename to libraries/shared/mocks/storage_keys_lookup.go
index d2f681bb3..033e68c83 100644
--- a/libraries/shared/mocks/storage_mappings.go
+++ b/libraries/shared/mocks/storage_keys_lookup.go
@@ -18,22 +18,21 @@ package mocks
import (
"github.com/ethereum/go-ethereum/common"
-
"github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
)
-type MockMappings struct {
+type MockStorageKeysLookup struct {
Metadata utils.StorageValueMetadata
LookupCalled bool
LookupErr error
}
-func (mappings *MockMappings) Lookup(key common.Hash) (utils.StorageValueMetadata, error) {
+func (mappings *MockStorageKeysLookup) Lookup(key common.Hash) (utils.StorageValueMetadata, error) {
mappings.LookupCalled = true
return mappings.Metadata, mappings.LookupErr
}
-func (*MockMappings) SetDB(db *postgres.DB) {
+func (*MockStorageKeysLookup) SetDB(db *postgres.DB) {
panic("implement me")
}
diff --git a/libraries/shared/storage/mappings.go b/libraries/shared/storage/utils/keys_loader.go
similarity index 70%
rename from libraries/shared/storage/mappings.go
rename to libraries/shared/storage/utils/keys_loader.go
index f8b089aef..0142b5644 100644
--- a/libraries/shared/storage/mappings.go
+++ b/libraries/shared/storage/utils/keys_loader.go
@@ -14,23 +14,14 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-package storage
+package utils
import (
- "math/big"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
-
- "github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
- "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
+ "math/big"
)
-type Mappings interface {
- Lookup(key common.Hash) (utils.StorageValueMetadata, error)
- SetDB(db *postgres.DB)
-}
-
const (
IndexZero = "0000000000000000000000000000000000000000000000000000000000000000"
IndexOne = "0000000000000000000000000000000000000000000000000000000000000001"
@@ -46,32 +37,17 @@ const (
IndexEleven = "000000000000000000000000000000000000000000000000000000000000000b"
)
-func AddHashedKeys(currentMappings map[common.Hash]utils.StorageValueMetadata) map[common.Hash]utils.StorageValueMetadata {
- copyOfCurrentMappings := make(map[common.Hash]utils.StorageValueMetadata)
- for k, v := range currentMappings {
- copyOfCurrentMappings[k] = v
- }
- for k, v := range copyOfCurrentMappings {
- currentMappings[hashKey(k)] = v
- }
- return currentMappings
-}
-
-func hashKey(key common.Hash) common.Hash {
- return crypto.Keccak256Hash(key.Bytes())
-}
-
-func GetMapping(indexOnContract, key string) common.Hash {
+func GetStorageKeyForMapping(indexOnContract, key string) common.Hash {
keyBytes := common.FromHex(key + indexOnContract)
return crypto.Keccak256Hash(keyBytes)
}
-func GetNestedMapping(indexOnContract, primaryKey, secondaryKey string) common.Hash {
+func GetStorageKeyForNestedMapping(indexOnContract, primaryKey, secondaryKey string) common.Hash {
primaryMappingIndex := crypto.Keccak256(common.FromHex(primaryKey + indexOnContract))
return crypto.Keccak256Hash(common.FromHex(secondaryKey), primaryMappingIndex)
}
-func GetIncrementedKey(original common.Hash, incrementBy int64) common.Hash {
+func GetIncrementedStorageKey(original common.Hash, incrementBy int64) common.Hash {
originalMappingAsInt := original.Big()
incremented := big.NewInt(0).Add(originalMappingAsInt, big.NewInt(incrementBy))
return common.BytesToHash(incremented.Bytes())
diff --git a/libraries/shared/storage/mappings_test.go b/libraries/shared/storage/utils/keys_loader_test.go
similarity index 59%
rename from libraries/shared/storage/mappings_test.go
rename to libraries/shared/storage/utils/keys_loader_test.go
index bc077f1f4..51ad58fd6 100644
--- a/libraries/shared/storage/mappings_test.go
+++ b/libraries/shared/storage/utils/keys_loader_test.go
@@ -1,78 +1,72 @@
-package storage_test
+// VulcanizeDB
+// Copyright © 2019 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 .
+
+package utils_test
import (
"github.com/ethereum/go-ethereum/common"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
- "github.com/vulcanize/vulcanizedb/libraries/shared/storage"
"github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
)
-var _ = Describe("Mappings", func() {
- Describe("AddHashedKeys", func() {
- It("returns a copy of the map with an additional slot for the hashed version of every key", func() {
- fakeMap := map[common.Hash]utils.StorageValueMetadata{}
- fakeStorageKey := common.HexToHash("72c72de6b203d67cb6cd54fc93300109fcc6fd6eac88e390271a3d548794d800")
- var fakeMappingKey utils.Key = "fakeKey"
- fakeMetadata := utils.StorageValueMetadata{
- Name: "fakeName",
- Keys: map[utils.Key]string{fakeMappingKey: "fakeValue"},
- Type: utils.Uint48,
- }
- fakeMap[fakeStorageKey] = fakeMetadata
-
- result := storage.AddHashedKeys(fakeMap)
-
- Expect(len(result)).To(Equal(2))
- expectedHashedStorageKey := common.HexToHash("2165edb4e1c37b99b60fa510d84f939dd35d5cd1d1c8f299d6456ea09df65a76")
- Expect(fakeMap[fakeStorageKey]).To(Equal(fakeMetadata))
- Expect(fakeMap[expectedHashedStorageKey]).To(Equal(fakeMetadata))
- })
- })
-
- Describe("GetMapping", func() {
+var _ = Describe("Storage keys loader utils", func() {
+ Describe("GetStorageKeyForMapping", func() {
It("returns the storage key for a mapping when passed the mapping's index on the contract and the desired value's key", func() {
// ex. solidity:
// mapping (bytes32 => uint) public amounts
// to access amounts, pass in the index of the mapping on the contract + the bytes32 key for the uint val being looked up
- indexOfMappingOnContract := storage.IndexZero
+ indexOfMappingOnContract := utils.IndexZero
keyForDesiredValueInMapping := "1234567890abcdef"
- storageKey := storage.GetMapping(indexOfMappingOnContract, keyForDesiredValueInMapping)
+ storageKey := utils.GetStorageKeyForMapping(indexOfMappingOnContract, keyForDesiredValueInMapping)
expectedStorageKey := common.HexToHash("0xee0c1b59a3856bafbfb8730e7694c4badc271eb5f01ce4a8d7a53d8a6499676f")
Expect(storageKey).To(Equal(expectedStorageKey))
})
It("returns same result if value includes hex prefix", func() {
- indexOfMappingOnContract := storage.IndexZero
+ indexOfMappingOnContract := utils.IndexZero
keyForDesiredValueInMapping := "0x1234567890abcdef"
- storageKey := storage.GetMapping(indexOfMappingOnContract, keyForDesiredValueInMapping)
+ storageKey := utils.GetStorageKeyForMapping(indexOfMappingOnContract, keyForDesiredValueInMapping)
expectedStorageKey := common.HexToHash("0xee0c1b59a3856bafbfb8730e7694c4badc271eb5f01ce4a8d7a53d8a6499676f")
Expect(storageKey).To(Equal(expectedStorageKey))
})
})
- Describe("GetNestedMapping", func() {
+ Describe("GetStorageKeyForNestedMapping", func() {
It("returns the storage key for a nested mapping when passed the mapping's index on the contract and the desired value's keys", func() {
// ex. solidity:
// mapping (bytes32 => uint) public amounts
// mapping (address => mapping (uint => bytes32)) public addressNames
// to access addressNames, pass in the index of the mapping on the contract + the address and uint keys for the bytes32 val being looked up
- indexOfMappingOnContract := storage.IndexOne
+ indexOfMappingOnContract := utils.IndexOne
keyForOuterMapping := "1234567890abcdef"
keyForInnerMapping := "123"
- storageKey := storage.GetNestedMapping(indexOfMappingOnContract, keyForOuterMapping, keyForInnerMapping)
+ storageKey := utils.GetStorageKeyForNestedMapping(indexOfMappingOnContract, keyForOuterMapping, keyForInnerMapping)
expectedStorageKey := common.HexToHash("0x82113529f6cd61061d1a6f0de53f2bdd067a1addd3d2b46be50a99abfcdb1661")
Expect(storageKey).To(Equal(expectedStorageKey))
})
})
- Describe("GetIncrementedKey", func() {
+ Describe("GetIncrementedStorageKey", func() {
It("returns the storage key for later values sharing an index on the contract with other earlier values", func() {
// ex. solidity:
// mapping (bytes32 => uint) public amounts
@@ -84,11 +78,11 @@ var _ = Describe("Mappings", func() {
// mapping (bytes32 => Data) public itemData;
// to access quality from itemData, pass in the storage key for the zero-indexed value (quantity) + the number of increments required.
// (For "quality", we must increment the storage key for the corresponding "quantity" by 1).
- indexOfMappingOnContract := storage.IndexTwo
+ indexOfMappingOnContract := utils.IndexTwo
keyForDesiredValueInMapping := "1234567890abcdef"
- storageKeyForFirstPropertyOnStruct := storage.GetMapping(indexOfMappingOnContract, keyForDesiredValueInMapping)
+ storageKeyForFirstPropertyOnStruct := utils.GetStorageKeyForMapping(indexOfMappingOnContract, keyForDesiredValueInMapping)
- storageKey := storage.GetIncrementedKey(storageKeyForFirstPropertyOnStruct, 1)
+ storageKey := utils.GetIncrementedStorageKey(storageKeyForFirstPropertyOnStruct, 1)
expectedStorageKey := common.HexToHash("0x69b38749f0a8ed5d505c8474f7fb62c7828aad8a7627f1c67e07af1d2368cad4")
Expect(storageKey).To(Equal(expectedStorageKey))
diff --git a/libraries/shared/storage/utils/keys_lookup.go b/libraries/shared/storage/utils/keys_lookup.go
new file mode 100644
index 000000000..fd2c1ee81
--- /dev/null
+++ b/libraries/shared/storage/utils/keys_lookup.go
@@ -0,0 +1,37 @@
+// VulcanizeDB
+// Copyright © 2019 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 .
+
+package utils
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+)
+
+func AddHashedKeys(currentMappings map[common.Hash]StorageValueMetadata) map[common.Hash]StorageValueMetadata {
+ copyOfCurrentMappings := make(map[common.Hash]StorageValueMetadata)
+ for k, v := range currentMappings {
+ copyOfCurrentMappings[k] = v
+ }
+ for k, v := range copyOfCurrentMappings {
+ currentMappings[hashKey(k)] = v
+ }
+ return currentMappings
+}
+
+func hashKey(key common.Hash) common.Hash {
+ return crypto.Keccak256Hash(key.Bytes())
+}
diff --git a/libraries/shared/storage/utils/keys_lookup_test.go b/libraries/shared/storage/utils/keys_lookup_test.go
new file mode 100644
index 000000000..22ca16df8
--- /dev/null
+++ b/libraries/shared/storage/utils/keys_lookup_test.go
@@ -0,0 +1,47 @@
+// VulcanizeDB
+// Copyright © 2019 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 .
+
+package utils_test
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
+)
+
+var _ = Describe("Storage keys lookup utils", func() {
+ Describe("AddHashedKeys", func() {
+ It("returns a copy of the map with an additional slot for the hashed version of every key", func() {
+ fakeMap := map[common.Hash]utils.StorageValueMetadata{}
+ fakeStorageKey := common.HexToHash("72c72de6b203d67cb6cd54fc93300109fcc6fd6eac88e390271a3d548794d800")
+ var fakeMappingKey utils.Key = "fakeKey"
+ fakeMetadata := utils.StorageValueMetadata{
+ Name: "fakeName",
+ Keys: map[utils.Key]string{fakeMappingKey: "fakeValue"},
+ Type: utils.Uint48,
+ }
+ fakeMap[fakeStorageKey] = fakeMetadata
+
+ result := utils.AddHashedKeys(fakeMap)
+
+ Expect(len(result)).To(Equal(2))
+ expectedHashedStorageKey := common.HexToHash("2165edb4e1c37b99b60fa510d84f939dd35d5cd1d1c8f299d6456ea09df65a76")
+ Expect(fakeMap[fakeStorageKey]).To(Equal(fakeMetadata))
+ Expect(fakeMap[expectedHashedStorageKey]).To(Equal(fakeMetadata))
+ })
+ })
+})