eth-ipfs-state-validator/pkg/validator_test.go

342 lines
11 KiB
Go
Raw Normal View History

2020-07-12 18:57:30 +00:00
// VulcanizeDB
// Copyright © 2020 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 validator_test
import (
"math/big"
2022-08-24 19:09:46 +00:00
"os"
"path/filepath"
2020-07-12 18:57:30 +00:00
"github.com/ethereum/go-ethereum/common"
2021-10-11 15:09:03 +00:00
"github.com/ethereum/go-ethereum/core/types"
2020-07-12 18:57:30 +00:00
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
2021-10-11 15:18:45 +00:00
cid "github.com/ipfs/go-cid/_rsrch/cidiface"
2020-07-12 18:57:30 +00:00
"github.com/jmoiron/sqlx"
"github.com/multiformats/go-multihash"
. "github.com/onsi/ginkgo/v2"
2020-07-12 18:57:30 +00:00
. "github.com/onsi/gomega"
2023-04-29 06:40:03 +00:00
validator "github.com/cerc-io/eth-ipfs-state-validator/v5/pkg"
2020-07-12 18:57:30 +00:00
)
var (
2022-05-02 06:29:02 +00:00
blockNumber = uint64(1)
2020-07-12 18:57:30 +00:00
contractAddr = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476592")
slot0StorageValue = common.Hex2Bytes("94703c4b2bd70c169f5717101caee543299fc946c7")
slot1StorageValue = common.Hex2Bytes("01")
nullCodeHash = crypto.Keccak256Hash([]byte{})
emptyRootNode, _ = rlp.EncodeToBytes(&[]byte{})
2020-07-12 18:57:30 +00:00
emptyContractRoot = crypto.Keccak256Hash(emptyRootNode)
stateBranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
2020-07-12 18:57:30 +00:00
crypto.Keccak256(bankAccountLeafNode),
[]byte{},
[]byte{},
[]byte{},
[]byte{},
crypto.Keccak256(minerAccountLeafNode),
crypto.Keccak256(contractAccountLeafNode),
[]byte{},
[]byte{},
[]byte{},
[]byte{},
[]byte{},
crypto.Keccak256(account2LeafNode),
[]byte{},
crypto.Keccak256(account1LeafNode),
[]byte{},
[]byte{},
})
stateRoot = crypto.Keccak256Hash(stateBranchRootNode)
mockCode = []byte{1, 2, 3, 4, 5}
codeHash = crypto.Keccak256Hash(mockCode)
2022-08-30 14:48:12 +00:00
codePath = common.Hex2Bytes("6114658a74d9cc9f7acf2c5cd696c3494d7c344d78bfec3add0d91ec4e8d1c45")
contractAccount, _ = rlp.EncodeToBytes(&types.StateAccount{
2020-07-12 18:57:30 +00:00
Nonce: 1,
Balance: big.NewInt(0),
CodeHash: codeHash.Bytes(),
2020-07-12 18:57:30 +00:00
Root: crypto.Keccak256Hash(storageBranchRootNode),
})
contractAccountLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
2020-07-12 18:57:30 +00:00
common.Hex2Bytes("3114658a74d9cc9f7acf2c5cd696c3494d7c344d78bfec3add0d91ec4e8d1c45"),
contractAccount,
})
minerAccount, _ = rlp.EncodeToBytes(&types.StateAccount{
2020-07-12 18:57:30 +00:00
Nonce: 0,
Balance: big.NewInt(1000),
CodeHash: nullCodeHash.Bytes(),
Root: emptyContractRoot,
})
minerAccountLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
2020-07-12 18:57:30 +00:00
common.Hex2Bytes("3380c7b7ae81a58eb98d9c78de4a1fd7fd9535fc953ed2be602daaa41767312a"),
minerAccount,
})
account1, _ = rlp.EncodeToBytes(&types.StateAccount{
2020-07-12 18:57:30 +00:00
Nonce: 2,
Balance: big.NewInt(1000),
CodeHash: nullCodeHash.Bytes(),
Root: emptyContractRoot,
})
account1LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
2020-07-12 18:57:30 +00:00
common.Hex2Bytes("3926db69aaced518e9b9f0f434a473e7174109c943548bb8f23be41ca76d9ad2"),
account1,
})
account2, _ = rlp.EncodeToBytes(&types.StateAccount{
2020-07-12 18:57:30 +00:00
Nonce: 0,
Balance: big.NewInt(1000),
CodeHash: nullCodeHash.Bytes(),
Root: emptyContractRoot,
})
account2LeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
2020-07-12 18:57:30 +00:00
common.Hex2Bytes("3957f3e2f04a0764c3a0491b175f69926da61efbcc8f61fa1455fd2d2b4cdd45"),
account2,
})
bankAccount, _ = rlp.EncodeToBytes(&types.StateAccount{
2020-07-12 18:57:30 +00:00
Nonce: 2,
Balance: big.NewInt(1000),
CodeHash: nullCodeHash.Bytes(),
Root: emptyContractRoot,
})
bankAccountLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
2020-07-12 18:57:30 +00:00
common.Hex2Bytes("30bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a"),
bankAccount,
})
storageBranchRootNode, _ = rlp.EncodeToBytes(&[]interface{}{
2020-07-12 18:57:30 +00:00
[]byte{},
[]byte{},
crypto.Keccak256(slot0StorageLeafNode),
[]byte{},
[]byte{},
[]byte{},
[]byte{},
[]byte{},
[]byte{},
[]byte{},
[]byte{},
crypto.Keccak256(slot1StorageLeafNode),
[]byte{},
[]byte{},
[]byte{},
[]byte{},
[]byte{},
})
storageRoot = crypto.Keccak256Hash(storageBranchRootNode)
slot0StorageLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
2020-07-12 18:57:30 +00:00
common.Hex2Bytes("390decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"),
slot0StorageValue,
})
slot1StorageLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
2020-07-12 18:57:30 +00:00
common.Hex2Bytes("310e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"),
slot1StorageValue,
})
trieStateNodes = [][]byte{
stateBranchRootNode,
bankAccountLeafNode,
minerAccountLeafNode,
contractAccountLeafNode,
account1LeafNode,
account2LeafNode,
}
trieStorageNodes = [][]byte{
storageBranchRootNode,
slot0StorageLeafNode,
slot1StorageLeafNode,
}
missingRootStateNodes = [][]byte{
bankAccountLeafNode,
minerAccountLeafNode,
contractAccountLeafNode,
account1LeafNode,
account2LeafNode,
}
missingRootStorageNodes = [][]byte{
slot0StorageLeafNode,
slot1StorageLeafNode,
}
missingNodeStateNodes = [][]byte{
stateBranchRootNode,
bankAccountLeafNode,
minerAccountLeafNode,
contractAccountLeafNode,
account2LeafNode,
}
missingNodeStorageNodes = [][]byte{
storageBranchRootNode,
slot1StorageLeafNode,
}
2022-08-30 14:48:12 +00:00
missingStateNodePath = common.Hex2Bytes("0e")
missingStorageNodePath = common.Hex2Bytes("02")
2020-07-12 18:57:30 +00:00
)
var (
v *validator.Validator
db *sqlx.DB
err error
2022-08-24 19:09:46 +00:00
tmp string
2023-03-10 07:54:22 +00:00
config = validator.Config{
Hostname: "localhost",
Name: "cerc_testing",
2023-03-10 07:54:22 +00:00
User: "vdbm",
Password: "password",
Port: 8077,
}
2020-07-12 18:57:30 +00:00
)
2020-07-13 00:57:47 +00:00
var _ = Describe("PG-IPFS Validator", func() {
2020-07-12 18:57:30 +00:00
BeforeEach(func() {
2023-03-10 07:54:22 +00:00
err = validator.LoadEnv(&config)
Expect(err).ToNot(HaveOccurred())
db, err = sqlx.Connect("postgres", config.ConnString())
2020-07-12 18:57:30 +00:00
Expect(err).ToNot(HaveOccurred())
2022-08-24 19:09:46 +00:00
tmp, err = os.MkdirTemp("", "test_validator")
Expect(err).ToNot(HaveOccurred())
params := validator.Params{Workers: 4, RecoveryFormat: filepath.Join(tmp, "recover_%s")}
v = validator.NewPGIPFSValidator(db, params)
2020-07-12 18:57:30 +00:00
})
2021-10-11 15:18:45 +00:00
AfterEach(func() {
2022-08-24 19:09:46 +00:00
os.RemoveAll(tmp)
2021-10-11 15:18:45 +00:00
v.Close()
2023-03-10 07:54:39 +00:00
db.Close()
2021-10-11 15:18:45 +00:00
})
2020-07-12 18:57:30 +00:00
Describe("ValidateTrie", func() {
2020-07-13 00:57:47 +00:00
AfterEach(func() {
2023-03-16 00:28:52 +00:00
err = ResetTestDB(db)
2020-07-13 00:57:47 +00:00
Expect(err).ToNot(HaveOccurred())
})
2022-08-24 19:09:46 +00:00
It("Returns an error if the state root node is missing", func() {
// we write code to ethdb, there should probably be an EthCode IPLD codec
// but there isn't, and we don't need one here since blockstore keys are mh-derived
loadTrie(missingRootStateNodes, trieStorageNodes, mockCode)
2020-07-12 18:57:30 +00:00
err = v.ValidateTrie(stateRoot)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("missing trie node"))
2020-07-12 18:57:30 +00:00
})
It("Returns an error if the storage root node is missing", func() {
loadTrie(trieStateNodes, missingRootStorageNodes, mockCode)
2020-07-12 18:57:30 +00:00
err = v.ValidateTrie(stateRoot)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("missing trie node"))
2020-07-12 18:57:30 +00:00
})
It("Returns an error if the state trie is missing node(s)", func() {
loadTrie(missingNodeStateNodes, trieStorageNodes, mockCode)
2020-07-12 18:57:30 +00:00
err = v.ValidateTrie(stateRoot)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("missing trie node"))
Expect(err.Error()).To(ContainSubstring("%x", missingStateNodePath))
2020-07-12 18:57:30 +00:00
})
It("Returns an error if the storage trie is missing node(s)", func() {
loadTrie(trieStateNodes, missingNodeStorageNodes, mockCode)
2020-07-12 18:57:30 +00:00
err = v.ValidateTrie(stateRoot)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("missing trie node"))
Expect(err.Error()).To(ContainSubstring("%x", missingStorageNodePath))
2020-07-12 18:57:30 +00:00
})
It("Returns an error if contract code is missing", func() {
2020-07-12 18:57:30 +00:00
loadTrie(trieStateNodes, trieStorageNodes)
err = v.ValidateTrie(stateRoot)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("%x", codeHash))
Expect(err.Error()).To(ContainSubstring("%x", codePath))
})
It("Returns no error if the entire state (state trie and storage tries) can be validated", func() {
loadTrie(trieStateNodes, trieStorageNodes, mockCode)
err = v.ValidateTrie(stateRoot)
2020-07-12 18:57:30 +00:00
Expect(err).ToNot(HaveOccurred())
})
})
Describe("ValidateStateTrie", func() {
2020-07-13 00:57:47 +00:00
AfterEach(func() {
2023-03-16 00:28:52 +00:00
err = ResetTestDB(db)
2020-07-13 00:57:47 +00:00
Expect(err).ToNot(HaveOccurred())
})
2020-07-12 18:57:30 +00:00
It("Returns an error the state root node is missing", func() {
loadTrie(missingRootStateNodes, nil)
err = v.ValidateStateTrie(stateRoot)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("missing trie node"))
})
It("Returns an error if the entire state trie cannot be validated", func() {
loadTrie(missingNodeStateNodes, nil)
err = v.ValidateStateTrie(stateRoot)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("missing trie node"))
})
It("Returns no error if the entire state trie can be validated", func() {
loadTrie(trieStateNodes, nil)
err = v.ValidateStateTrie(stateRoot)
Expect(err).ToNot(HaveOccurred())
})
})
Describe("ValidateStorageTrie", func() {
2020-07-13 00:57:47 +00:00
AfterEach(func() {
2023-03-16 00:28:52 +00:00
err = ResetTestDB(db)
2020-07-13 00:57:47 +00:00
Expect(err).ToNot(HaveOccurred())
})
2020-07-12 18:57:30 +00:00
It("Returns an error the storage root node is missing", func() {
loadTrie(nil, missingRootStorageNodes)
err = v.ValidateStorageTrie(stateRoot, contractAddr, storageRoot)
2020-07-12 18:57:30 +00:00
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("missing trie node"))
})
It("Returns an error if the entire storage trie cannot be validated", func() {
loadTrie(nil, missingNodeStorageNodes)
err = v.ValidateStorageTrie(stateRoot, contractAddr, storageRoot)
2020-07-12 18:57:30 +00:00
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("missing trie node"))
})
It("Returns no error if the entire storage trie can be validated", func() {
loadTrie(nil, trieStorageNodes)
err = v.ValidateStorageTrie(stateRoot, contractAddr, storageRoot)
2020-07-12 18:57:30 +00:00
Expect(err).ToNot(HaveOccurred())
})
})
})
func loadTrie(stateNodes, storageNodes [][]byte, contractCode ...[]byte) {
2020-07-12 18:57:30 +00:00
tx, err := db.Beginx()
Expect(err).ToNot(HaveOccurred())
for _, node := range stateNodes {
err := PublishRaw(tx, cid.EthStateTrie, multihash.KECCAK_256, node, blockNumber)
2020-07-12 18:57:30 +00:00
Expect(err).ToNot(HaveOccurred())
}
for _, node := range storageNodes {
err := PublishRaw(tx, cid.EthStorageTrie, multihash.KECCAK_256, node, blockNumber)
Expect(err).ToNot(HaveOccurred())
}
for _, code := range contractCode {
err := PublishRaw(tx, cid.Raw, multihash.KECCAK_256, code, blockNumber)
2020-07-12 18:57:30 +00:00
Expect(err).ToNot(HaveOccurred())
}
err = tx.Commit()
Expect(err).ToNot(HaveOccurred())
}