Add command to take an in-place snapshot (#42)
* Add command to take an in-place snapshot * Add test data for in place snapshot unit test * Implement unit test for inplace snapshot * Add check for storage IPLD * Run unit tests sequentially * Add github workflow for unit test * Add missing checks for state and storage cid fields * Add more storage nodes to test * Update ipld-eth-db version for tests * Add comments for inplace snapshot test data * Add in-place snapshot cmd in readme * Implement defer pattern for db transaction * Log transaction commit or rollback error Co-authored-by: nabarun <nabarun@deepstacksoft.com>
This commit is contained in:
parent
c2ecea6d61
commit
b241bf05eb
30
.github/workflows/on-pr.yaml
vendored
Normal file
30
.github/workflows/on-pr.yaml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: Docker Build
|
||||||
|
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Run unit tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GOPATH: /tmp/go
|
||||||
|
GO111MODULE: on
|
||||||
|
steps:
|
||||||
|
- name: Create GOPATH
|
||||||
|
run: mkdir -p /tmp/go
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: ">=1.18.0"
|
||||||
|
check-latest: true
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Run database
|
||||||
|
run: docker-compose up -d
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: |
|
||||||
|
sleep 45
|
||||||
|
make dbtest
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
ipld-eth-state-snapshot
|
ipld-eth-state-snapshot
|
||||||
mocks/
|
mocks/
|
||||||
|
.vscode
|
||||||
|
5
Makefile
5
Makefile
@ -16,4 +16,7 @@ build:
|
|||||||
go build
|
go build
|
||||||
|
|
||||||
test: mocks
|
test: mocks
|
||||||
go clean -testcache && go test -v ./...
|
go clean -testcache && go test -p 1 -v ./...
|
||||||
|
|
||||||
|
dbtest: mocks
|
||||||
|
go clean -testcache && TEST_WITH_DB=true go test -p 1 -v ./...
|
||||||
|
@ -6,7 +6,15 @@
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
For state snapshot from LevelDB
|
||||||
|
```bash
|
||||||
./ipld-eth-state-snapshot stateSnapshot --config={path to toml config file}
|
./ipld-eth-state-snapshot stateSnapshot --config={path to toml config file}
|
||||||
|
```
|
||||||
|
|
||||||
|
For in-place snapshot in database
|
||||||
|
```bash
|
||||||
|
./ipld-eth-state-snapshot inPlaceStateSnapshot --config={path to toml config file}
|
||||||
|
```
|
||||||
|
|
||||||
### Config
|
### Config
|
||||||
|
|
||||||
|
63
cmd/inplaceStateSnapshot.go
Normal file
63
cmd/inplaceStateSnapshot.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// VulcanizeDB
|
||||||
|
// Copyright © 2022 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 cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/vulcanize/ipld-eth-state-snapshot/pkg/snapshot"
|
||||||
|
)
|
||||||
|
|
||||||
|
// inPlaceStateSnapshotCmd represents the inPlaceStateSnapshot command
|
||||||
|
var inPlaceStateSnapshotCmd = &cobra.Command{
|
||||||
|
Use: "inPlaceStateSnapshot",
|
||||||
|
Short: "Take an in-place state snapshot in the database",
|
||||||
|
Long: `Usage:
|
||||||
|
|
||||||
|
./ipld-eth-state-snapshot inPlaceStateSnapshot --config={path to toml config file}`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
subCommand = cmd.CalledAs()
|
||||||
|
logWithCommand = *logrus.WithField("SubCommand", subCommand)
|
||||||
|
inPlaceStateSnapshot()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func inPlaceStateSnapshot() {
|
||||||
|
config := snapshot.NewInPlaceSnapshotConfig()
|
||||||
|
|
||||||
|
startHeight := viper.GetUint64(snapshot.SNAPSHOT_START_HEIGHT_TOML)
|
||||||
|
endHeight := viper.GetUint64(snapshot.SNAPSHOT_END_HEIGHT_TOML)
|
||||||
|
|
||||||
|
params := snapshot.InPlaceSnapshotParams{StartHeight: uint64(startHeight), EndHeight: uint64(endHeight)}
|
||||||
|
if err := snapshot.CreateInPlaceSnapshot(config, params); err != nil {
|
||||||
|
logWithCommand.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logWithCommand.Infof("snapshot taken at height %d starting from height %d", endHeight, startHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(inPlaceStateSnapshotCmd)
|
||||||
|
|
||||||
|
inPlaceStateSnapshotCmd.PersistentFlags().String(snapshot.SNAPSHOT_START_HEIGHT_CLI, "", "start block height for in-place snapshot")
|
||||||
|
inPlaceStateSnapshotCmd.PersistentFlags().String(snapshot.SNAPSHOT_END_HEIGHT_CLI, "", "end block height for in-place snapshot")
|
||||||
|
|
||||||
|
viper.BindPFlag(snapshot.SNAPSHOT_START_HEIGHT_TOML, inPlaceStateSnapshotCmd.PersistentFlags().Lookup(snapshot.SNAPSHOT_START_HEIGHT_CLI))
|
||||||
|
viper.BindPFlag(snapshot.SNAPSHOT_END_HEIGHT_TOML, inPlaceStateSnapshotCmd.PersistentFlags().Lookup(snapshot.SNAPSHOT_END_HEIGHT_CLI))
|
||||||
|
}
|
27
docker-compose.yml
Normal file
27
docker-compose.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
version: '3.2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
migrations:
|
||||||
|
restart: on-failure
|
||||||
|
depends_on:
|
||||||
|
- ipld-eth-db
|
||||||
|
image: vulcanize/ipld-eth-db:v4.1.3-alpha
|
||||||
|
environment:
|
||||||
|
DATABASE_USER: "vdbm"
|
||||||
|
DATABASE_NAME: "vulcanize_testing"
|
||||||
|
DATABASE_PASSWORD: "password"
|
||||||
|
DATABASE_HOSTNAME: "ipld-eth-db"
|
||||||
|
DATABASE_PORT: 5432
|
||||||
|
|
||||||
|
ipld-eth-db:
|
||||||
|
image: timescale/timescaledb:latest-pg14
|
||||||
|
restart: always
|
||||||
|
command: ["postgres", "-c", "log_statement=all"]
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: "vdbm"
|
||||||
|
POSTGRES_DB: "vulcanize_testing"
|
||||||
|
POSTGRES_PASSWORD: "password"
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8077:5432"
|
||||||
|
volumes:
|
||||||
|
- /tmp:/tmp
|
@ -25,6 +25,9 @@
|
|||||||
blockHeight = -1
|
blockHeight = -1
|
||||||
recoveryFile = "recovery_file"
|
recoveryFile = "recovery_file"
|
||||||
|
|
||||||
|
startHeight = 1
|
||||||
|
endHeight = 12
|
||||||
|
|
||||||
[file]
|
[file]
|
||||||
outputDir = "output_dir/"
|
outputDir = "output_dir/"
|
||||||
|
|
||||||
|
307
fixture/in_place_snapshot.go
Normal file
307
fixture/in_place_snapshot.go
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
// Copyright © 2022 Vulcanize, Inc
|
||||||
|
//
|
||||||
|
// 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 fixture
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Block struct {
|
||||||
|
Hash common.Hash
|
||||||
|
Number *big.Int
|
||||||
|
StateNodes []snapt.Node
|
||||||
|
StorageNodes [][]snapt.Node
|
||||||
|
}
|
||||||
|
|
||||||
|
var InPlaceSnapshotBlocks = []Block{
|
||||||
|
// Genesis block
|
||||||
|
{
|
||||||
|
Hash: common.HexToHash("0xe1bdb963128f645aa674b52a8c7ce00704762f27e2a6896abebd7954878f40e4"),
|
||||||
|
Number: big.NewInt(0),
|
||||||
|
StateNodes: []snapt.Node{
|
||||||
|
// State node for main account with balance.
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{},
|
||||||
|
Key: common.HexToHash("0x67ab3c0dd775f448af7fb41243415ed6fb975d1530a2d828f69bea7346231ad7"),
|
||||||
|
Value: []byte{248, 119, 161, 32, 103, 171, 60, 13, 215, 117, 244, 72, 175, 127, 180, 18, 67, 65, 94, 214, 251, 151, 93, 21, 48, 162, 216, 40, 246, 155, 234, 115, 70, 35, 26, 215, 184, 83, 248, 81, 128, 141, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Contract Test1 deployed by main account.
|
||||||
|
{
|
||||||
|
Hash: common.HexToHash("0x46ce57b700e470d0c0820ede662ecc0d0c78cf87237cb12a40a7ff5ff9cc8ac5"),
|
||||||
|
Number: big.NewInt(1),
|
||||||
|
StateNodes: []snapt.Node{
|
||||||
|
// Branch root node.
|
||||||
|
{
|
||||||
|
NodeType: 0,
|
||||||
|
Path: []byte{},
|
||||||
|
Value: []byte{248, 81, 128, 128, 128, 128, 128, 128, 160, 173, 52, 73, 195, 118, 160, 81, 100, 138, 50, 127, 27, 188, 85, 147, 215, 187, 244, 219, 228, 93, 25, 72, 253, 160, 45, 16, 239, 130, 223, 160, 26, 128, 128, 160, 137, 52, 229, 60, 211, 96, 171, 177, 51, 19, 204, 180, 24, 252, 28, 70, 234, 7, 73, 20, 117, 230, 32, 223, 188, 6, 191, 75, 123, 64, 163, 197, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
// State node for sender account.
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{6},
|
||||||
|
Key: common.HexToHash("0x67ab3c0dd775f448af7fb41243415ed6fb975d1530a2d828f69bea7346231ad7"),
|
||||||
|
Value: []byte{248, 118, 160, 55, 171, 60, 13, 215, 117, 244, 72, 175, 127, 180, 18, 67, 65, 94, 214, 251, 151, 93, 21, 48, 162, 216, 40, 246, 155, 234, 115, 70, 35, 26, 215, 184, 83, 248, 81, 1, 141, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112},
|
||||||
|
},
|
||||||
|
// State node for deployment of contract Test1.
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{9},
|
||||||
|
Key: common.HexToHash("0x9397e33dedda4763aea143fc6151ebcd9a93f62db7a6a556d46c585d82ad2afc"),
|
||||||
|
Value: []byte{248, 105, 160, 51, 151, 227, 61, 237, 218, 71, 99, 174, 161, 67, 252, 97, 81, 235, 205, 154, 147, 246, 45, 183, 166, 165, 86, 212, 108, 88, 93, 130, 173, 42, 252, 184, 70, 248, 68, 1, 128, 160, 243, 143, 159, 99, 199, 96, 208, 136, 215, 221, 4, 247, 67, 97, 155, 98, 145, 246, 59, 238, 189, 139, 223, 83, 6, 40, 249, 14, 156, 250, 82, 215, 160, 47, 62, 207, 242, 160, 167, 130, 233, 6, 187, 196, 80, 96, 6, 188, 150, 74, 176, 201, 7, 65, 32, 174, 97, 1, 76, 26, 86, 141, 49, 62, 214},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
StorageNodes: [][]snapt.Node{
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
// Storage node for contract Test1 state variable initialCount.
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{},
|
||||||
|
Key: common.HexToHash("0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"),
|
||||||
|
Value: []byte{227, 161, 32, 177, 14, 45, 82, 118, 18, 7, 59, 38, 238, 205, 253, 113, 126, 106, 50, 12, 244, 75, 74, 250, 194, 176, 115, 45, 159, 203, 226, 183, 250, 12, 246, 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Contract Test2 deployed by main account.
|
||||||
|
{
|
||||||
|
Hash: common.HexToHash("0xa848b156fe4e61d8dac0a833720794e8c58e93fa6db369af6f0d9a19ada9d723"),
|
||||||
|
Number: big.NewInt(2),
|
||||||
|
StateNodes: []snapt.Node{
|
||||||
|
// Branch root node.
|
||||||
|
{
|
||||||
|
NodeType: 0,
|
||||||
|
Path: []byte{},
|
||||||
|
Value: []byte{248, 81, 128, 128, 128, 128, 128, 128, 160, 191, 248, 9, 223, 101, 212, 255, 213, 196, 146, 160, 239, 69, 178, 134, 139, 81, 22, 255, 149, 90, 253, 178, 172, 102, 87, 249, 225, 224, 173, 183, 55, 128, 128, 160, 165, 200, 234, 64, 112, 157, 130, 31, 236, 38, 20, 68, 99, 247, 81, 161, 76, 62, 186, 246, 84, 121, 39, 155, 102, 134, 188, 109, 89, 220, 31, 212, 128, 128, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
// State node for sender account.
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{6},
|
||||||
|
Key: common.HexToHash("0x67ab3c0dd775f448af7fb41243415ed6fb975d1530a2d828f69bea7346231ad7"),
|
||||||
|
Value: []byte{248, 118, 160, 55, 171, 60, 13, 215, 117, 244, 72, 175, 127, 180, 18, 67, 65, 94, 214, 251, 151, 93, 21, 48, 162, 216, 40, 246, 155, 234, 115, 70, 35, 26, 215, 184, 83, 248, 81, 2, 141, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112},
|
||||||
|
},
|
||||||
|
// State node for deployment of contract Test2.
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{10},
|
||||||
|
Key: common.HexToHash("0xa44b5f4b47ded891709350af6a6e4d56602228a70279bdad4f0f64042445b4b9"),
|
||||||
|
Value: []byte{248, 105, 160, 52, 75, 95, 75, 71, 222, 216, 145, 112, 147, 80, 175, 106, 110, 77, 86, 96, 34, 40, 167, 2, 121, 189, 173, 79, 15, 100, 4, 36, 69, 180, 185, 184, 70, 248, 68, 1, 128, 160, 130, 30, 37, 86, 162, 144, 200, 100, 5, 248, 22, 10, 45, 102, 32, 66, 164, 49, 186, 69, 107, 157, 178, 101, 199, 155, 184, 55, 192, 75, 229, 240, 160, 86, 36, 245, 233, 5, 167, 42, 118, 181, 35, 178, 216, 149, 56, 146, 147, 19, 8, 140, 137, 234, 0, 160, 27, 220, 33, 204, 6, 152, 239, 177, 52},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
StorageNodes: [][]snapt.Node{
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
// Storage node for contract Test2 state variable test.
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{},
|
||||||
|
Key: common.HexToHash("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"),
|
||||||
|
Value: []byte{227, 161, 32, 41, 13, 236, 217, 84, 139, 98, 168, 214, 3, 69, 169, 136, 56, 111, 200, 75, 166, 188, 149, 72, 64, 8, 246, 54, 47, 147, 22, 14, 243, 229, 99, 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Increment contract Test1 state variable count using main account.
|
||||||
|
{
|
||||||
|
Hash: common.HexToHash("0x9fc4aaaab26f0b43ac609c99ae50925e5dc9a25f103c0511fcff38c6b3158302"),
|
||||||
|
Number: big.NewInt(3),
|
||||||
|
StateNodes: []snapt.Node{
|
||||||
|
// Branch root node.
|
||||||
|
{
|
||||||
|
NodeType: 0,
|
||||||
|
Path: []byte{},
|
||||||
|
Value: []byte{248, 113, 128, 128, 128, 128, 128, 128, 160, 70, 53, 190, 199, 124, 254, 86, 213, 42, 126, 117, 155, 2, 223, 56, 167, 130, 118, 10, 150, 65, 46, 207, 169, 167, 250, 209, 64, 37, 205, 153, 51, 128, 128, 160, 165, 200, 234, 64, 112, 157, 130, 31, 236, 38, 20, 68, 99, 247, 81, 161, 76, 62, 186, 246, 84, 121, 39, 155, 102, 134, 188, 109, 89, 220, 31, 212, 128, 128, 160, 214, 109, 199, 206, 145, 11, 213, 44, 206, 214, 36, 181, 134, 92, 243, 178, 58, 88, 158, 42, 31, 125, 71, 148, 188, 122, 252, 100, 250, 182, 85, 159, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
// State node for sender account.
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{6},
|
||||||
|
Key: common.HexToHash("0x67ab3c0dd775f448af7fb41243415ed6fb975d1530a2d828f69bea7346231ad7"),
|
||||||
|
Value: []byte{248, 118, 160, 55, 171, 60, 13, 215, 117, 244, 72, 175, 127, 180, 18, 67, 65, 94, 214, 251, 151, 93, 21, 48, 162, 216, 40, 246, 155, 234, 115, 70, 35, 26, 215, 184, 83, 248, 81, 3, 141, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112},
|
||||||
|
},
|
||||||
|
// State node for contract Test1 transaction.
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{9},
|
||||||
|
Key: common.HexToHash("0x9397e33dedda4763aea143fc6151ebcd9a93f62db7a6a556d46c585d82ad2afc"),
|
||||||
|
Value: []byte{248, 105, 160, 51, 151, 227, 61, 237, 218, 71, 99, 174, 161, 67, 252, 97, 81, 235, 205, 154, 147, 246, 45, 183, 166, 165, 86, 212, 108, 88, 93, 130, 173, 42, 252, 184, 70, 248, 68, 1, 128, 160, 167, 171, 204, 110, 30, 52, 74, 189, 215, 97, 245, 227, 176, 141, 250, 205, 8, 182, 138, 101, 51, 150, 155, 174, 234, 246, 30, 128, 253, 230, 36, 228, 160, 47, 62, 207, 242, 160, 167, 130, 233, 6, 187, 196, 80, 96, 6, 188, 150, 74, 176, 201, 7, 65, 32, 174, 97, 1, 76, 26, 86, 141, 49, 62, 214},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
StorageNodes: [][]snapt.Node{
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
// Branch root node.
|
||||||
|
{
|
||||||
|
NodeType: 0,
|
||||||
|
Path: []byte{},
|
||||||
|
Value: []byte{248, 81, 128, 128, 160, 79, 197, 241, 58, 178, 249, 186, 12, 45, 168, 139, 1, 81, 171, 14, 124, 244, 216, 93, 8, 204, 164, 92, 205, 146, 60, 106, 183, 99, 35, 235, 40, 128, 128, 128, 128, 128, 128, 128, 128, 160, 244, 152, 74, 17, 246, 26, 41, 33, 69, 97, 65, 223, 136, 222, 110, 26, 113, 13, 40, 104, 27, 145, 175, 121, 76, 90, 114, 30, 71, 131, 156, 215, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
// Storage node for contract Test1 state variable count.
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{2},
|
||||||
|
Key: common.HexToHash("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"),
|
||||||
|
Value: []byte{226, 160, 57, 13, 236, 217, 84, 139, 98, 168, 214, 3, 69, 169, 136, 56, 111, 200, 75, 166, 188, 149, 72, 64, 8, 246, 54, 47, 147, 22, 14, 243, 229, 99, 1},
|
||||||
|
},
|
||||||
|
// Storage node for contract Test1 state variable initialCount.
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{11},
|
||||||
|
Key: common.HexToHash("0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"),
|
||||||
|
Value: []byte{226, 160, 49, 14, 45, 82, 118, 18, 7, 59, 38, 238, 205, 253, 113, 126, 106, 50, 12, 244, 75, 74, 250, 194, 176, 115, 45, 159, 203, 226, 183, 250, 12, 246, 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expected state nodes at snapshot height.
|
||||||
|
var ExpectedStateNodes = []snapt.Node{
|
||||||
|
{
|
||||||
|
NodeType: 0,
|
||||||
|
Path: []byte{},
|
||||||
|
Value: []byte{248, 113, 128, 128, 128, 128, 128, 128, 160, 70, 53, 190, 199, 124, 254, 86, 213, 42, 126, 117, 155, 2, 223, 56, 167, 130, 118, 10, 150, 65, 46, 207, 169, 167, 250, 209, 64, 37, 205, 153, 51, 128, 128, 160, 165, 200, 234, 64, 112, 157, 130, 31, 236, 38, 20, 68, 99, 247, 81, 161, 76, 62, 186, 246, 84, 121, 39, 155, 102, 134, 188, 109, 89, 220, 31, 212, 128, 128, 160, 214, 109, 199, 206, 145, 11, 213, 44, 206, 214, 36, 181, 134, 92, 243, 178, 58, 88, 158, 42, 31, 125, 71, 148, 188, 122, 252, 100, 250, 182, 85, 159, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{6},
|
||||||
|
Key: common.HexToHash("0x67ab3c0dd775f448af7fb41243415ed6fb975d1530a2d828f69bea7346231ad7"),
|
||||||
|
Value: []byte{248, 118, 160, 55, 171, 60, 13, 215, 117, 244, 72, 175, 127, 180, 18, 67, 65, 94, 214, 251, 151, 93, 21, 48, 162, 216, 40, 246, 155, 234, 115, 70, 35, 26, 215, 184, 83, 248, 81, 3, 141, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, 160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{9},
|
||||||
|
Key: common.HexToHash("0x9397e33dedda4763aea143fc6151ebcd9a93f62db7a6a556d46c585d82ad2afc"),
|
||||||
|
Value: []byte{248, 105, 160, 51, 151, 227, 61, 237, 218, 71, 99, 174, 161, 67, 252, 97, 81, 235, 205, 154, 147, 246, 45, 183, 166, 165, 86, 212, 108, 88, 93, 130, 173, 42, 252, 184, 70, 248, 68, 1, 128, 160, 167, 171, 204, 110, 30, 52, 74, 189, 215, 97, 245, 227, 176, 141, 250, 205, 8, 182, 138, 101, 51, 150, 155, 174, 234, 246, 30, 128, 253, 230, 36, 228, 160, 47, 62, 207, 242, 160, 167, 130, 233, 6, 187, 196, 80, 96, 6, 188, 150, 74, 176, 201, 7, 65, 32, 174, 97, 1, 76, 26, 86, 141, 49, 62, 214},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{10},
|
||||||
|
Key: common.HexToHash("0xa44b5f4b47ded891709350af6a6e4d56602228a70279bdad4f0f64042445b4b9"),
|
||||||
|
Value: []byte{248, 105, 160, 52, 75, 95, 75, 71, 222, 216, 145, 112, 147, 80, 175, 106, 110, 77, 86, 96, 34, 40, 167, 2, 121, 189, 173, 79, 15, 100, 4, 36, 69, 180, 185, 184, 70, 248, 68, 1, 128, 160, 130, 30, 37, 86, 162, 144, 200, 100, 5, 248, 22, 10, 45, 102, 32, 66, 164, 49, 186, 69, 107, 157, 178, 101, 199, 155, 184, 55, 192, 75, 229, 240, 160, 86, 36, 245, 233, 5, 167, 42, 118, 181, 35, 178, 216, 149, 56, 146, 147, 19, 8, 140, 137, 234, 0, 160, 27, 220, 33, 204, 6, 152, 239, 177, 52},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type StorageNodeWithState struct {
|
||||||
|
snapt.Node
|
||||||
|
StatePath []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expected storage nodes at snapshot height.
|
||||||
|
var ExpectedStorageNodes = []StorageNodeWithState{
|
||||||
|
{
|
||||||
|
Node: snapt.Node{
|
||||||
|
NodeType: 0,
|
||||||
|
Path: []byte{},
|
||||||
|
Value: []byte{248, 81, 128, 128, 160, 79, 197, 241, 58, 178, 249, 186, 12, 45, 168, 139, 1, 81, 171, 14, 124, 244, 216, 93, 8, 204, 164, 92, 205, 146, 60, 106, 183, 99, 35, 235, 40, 128, 128, 128, 128, 128, 128, 128, 128, 160, 244, 152, 74, 17, 246, 26, 41, 33, 69, 97, 65, 223, 136, 222, 110, 26, 113, 13, 40, 104, 27, 145, 175, 121, 76, 90, 114, 30, 71, 131, 156, 215, 128, 128, 128, 128, 128},
|
||||||
|
},
|
||||||
|
StatePath: []byte{9},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Node: snapt.Node{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{2},
|
||||||
|
Key: common.HexToHash("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"),
|
||||||
|
Value: []byte{226, 160, 57, 13, 236, 217, 84, 139, 98, 168, 214, 3, 69, 169, 136, 56, 111, 200, 75, 166, 188, 149, 72, 64, 8, 246, 54, 47, 147, 22, 14, 243, 229, 99, 1},
|
||||||
|
},
|
||||||
|
StatePath: []byte{9},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Node: snapt.Node{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{11},
|
||||||
|
Key: common.HexToHash("0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"),
|
||||||
|
Value: []byte{226, 160, 49, 14, 45, 82, 118, 18, 7, 59, 38, 238, 205, 253, 113, 126, 106, 50, 12, 244, 75, 74, 250, 194, 176, 115, 45, 159, 203, 226, 183, 250, 12, 246, 1},
|
||||||
|
},
|
||||||
|
StatePath: []byte{9},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Node: snapt.Node{
|
||||||
|
NodeType: 2,
|
||||||
|
Path: []byte{},
|
||||||
|
Key: common.HexToHash("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"),
|
||||||
|
Value: []byte{227, 161, 32, 41, 13, 236, 217, 84, 139, 98, 168, 214, 3, 69, 169, 136, 56, 111, 200, 75, 166, 188, 149, 72, 64, 8, 246, 54, 47, 147, 22, 14, 243, 229, 99, 1},
|
||||||
|
},
|
||||||
|
StatePath: []byte{10},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block header at snapshot height.
|
||||||
|
// Required in database when executing inPlaceStateSnapshot.
|
||||||
|
var Block4_Header = types.Header{
|
||||||
|
ParentHash: common.HexToHash("0x9fc4aaaab26f0b43ac609c99ae50925e5dc9a25f103c0511fcff38c6b3158302"),
|
||||||
|
UncleHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
||||||
|
Coinbase: common.HexToAddress("0x0000000000000000000000000000000000000000"),
|
||||||
|
Root: common.HexToHash("0x53580584816f617295ea26c0e17641e0120cab2f0a8ffb53a866fd53aa8e8c2d"),
|
||||||
|
TxHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
|
||||||
|
ReceiptHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
|
||||||
|
Bloom: types.Bloom{},
|
||||||
|
Difficulty: big.NewInt(+2),
|
||||||
|
Number: big.NewInt(4),
|
||||||
|
GasLimit: 4704588,
|
||||||
|
GasUsed: 0,
|
||||||
|
Time: 1492010458,
|
||||||
|
Extra: []byte{215, 131, 1, 6, 0, 132, 103, 101, 116, 104, 135, 103, 111, 49, 46, 55, 46, 51, 133, 108, 105, 110, 117, 120, 0, 0, 0, 0, 0, 0, 0, 0, 159, 30, 250, 30, 250, 114, 175, 19, 140, 145, 89, 102, 198, 57, 84, 74, 2, 85, 230, 40, 142, 24, 140, 34, 206, 145, 104, 193, 13, 190, 70, 218, 61, 136, 180, 170, 6, 89, 48, 17, 159, 184, 134, 33, 11, 240, 26, 8, 79, 222, 93, 59, 196, 141, 138, 163, 139, 202, 146, 228, 252, 197, 33, 81, 0},
|
||||||
|
MixDigest: common.Hash{},
|
||||||
|
Nonce: types.BlockNonce{},
|
||||||
|
BaseFee: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
contract Test1 {
|
||||||
|
uint256 private count;
|
||||||
|
uint256 private initialCount;
|
||||||
|
|
||||||
|
event Increment(uint256 count);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
initialCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function incrementCount() public returns (uint256) {
|
||||||
|
count = count + 1;
|
||||||
|
emit Increment(count);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
contract Test2 {
|
||||||
|
uint256 private test;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
test = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
@ -69,6 +69,17 @@ func NewConfig(mode SnapshotMode) (*Config, error) {
|
|||||||
return ret, ret.Init(mode)
|
return ret, ret.Init(mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewInPlaceSnapshotConfig() *Config {
|
||||||
|
ret := &Config{
|
||||||
|
&EthConfig{},
|
||||||
|
&DBConfig{},
|
||||||
|
&FileConfig{},
|
||||||
|
}
|
||||||
|
ret.DB.Init()
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
// Init Initialises config
|
// Init Initialises config
|
||||||
func (c *Config) Init(mode SnapshotMode) error {
|
func (c *Config) Init(mode SnapshotMode) error {
|
||||||
viper.BindEnv(ETH_NODE_ID_TOML, ETH_NODE_ID)
|
viper.BindEnv(ETH_NODE_ID_TOML, ETH_NODE_ID)
|
||||||
|
@ -21,6 +21,8 @@ const (
|
|||||||
SNAPSHOT_WORKERS = "SNAPSHOT_WORKERS"
|
SNAPSHOT_WORKERS = "SNAPSHOT_WORKERS"
|
||||||
SNAPSHOT_RECOVERY_FILE = "SNAPSHOT_RECOVERY_FILE"
|
SNAPSHOT_RECOVERY_FILE = "SNAPSHOT_RECOVERY_FILE"
|
||||||
SNAPSHOT_MODE = "SNAPSHOT_MODE"
|
SNAPSHOT_MODE = "SNAPSHOT_MODE"
|
||||||
|
SNAPSHOT_START_HEIGHT = "SNAPSHOT_START_HEIGHT"
|
||||||
|
SNAPSHOT_END_HEIGHT = "SNAPSHOT_END_HEIGHT"
|
||||||
|
|
||||||
LOGRUS_LEVEL = "LOGRUS_LEVEL"
|
LOGRUS_LEVEL = "LOGRUS_LEVEL"
|
||||||
LOGRUS_FILE = "LOGRUS_FILE"
|
LOGRUS_FILE = "LOGRUS_FILE"
|
||||||
@ -58,6 +60,8 @@ const (
|
|||||||
SNAPSHOT_WORKERS_TOML = "snapshot.workers"
|
SNAPSHOT_WORKERS_TOML = "snapshot.workers"
|
||||||
SNAPSHOT_RECOVERY_FILE_TOML = "snapshot.recoveryFile"
|
SNAPSHOT_RECOVERY_FILE_TOML = "snapshot.recoveryFile"
|
||||||
SNAPSHOT_MODE_TOML = "snapshot.mode"
|
SNAPSHOT_MODE_TOML = "snapshot.mode"
|
||||||
|
SNAPSHOT_START_HEIGHT_TOML = "snapshot.startHeight"
|
||||||
|
SNAPSHOT_END_HEIGHT_TOML = "snapshot.endHeight"
|
||||||
|
|
||||||
LOGRUS_LEVEL_TOML = "log.level"
|
LOGRUS_LEVEL_TOML = "log.level"
|
||||||
LOGRUS_FILE_TOML = "log.file"
|
LOGRUS_FILE_TOML = "log.file"
|
||||||
@ -95,6 +99,8 @@ const (
|
|||||||
SNAPSHOT_WORKERS_CLI = "workers"
|
SNAPSHOT_WORKERS_CLI = "workers"
|
||||||
SNAPSHOT_RECOVERY_FILE_CLI = "recovery-file"
|
SNAPSHOT_RECOVERY_FILE_CLI = "recovery-file"
|
||||||
SNAPSHOT_MODE_CLI = "snapshot-mode"
|
SNAPSHOT_MODE_CLI = "snapshot-mode"
|
||||||
|
SNAPSHOT_START_HEIGHT_CLI = "start-height"
|
||||||
|
SNAPSHOT_END_HEIGHT_CLI = "end-height"
|
||||||
|
|
||||||
LOGRUS_LEVEL_CLI = "log-level"
|
LOGRUS_LEVEL_CLI = "log-level"
|
||||||
LOGRUS_FILE_CLI = "log-file"
|
LOGRUS_FILE_CLI = "log-file"
|
||||||
|
@ -9,8 +9,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql"
|
||||||
|
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
|
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
|
||||||
"github.com/jackc/pgx/v4"
|
|
||||||
|
|
||||||
fixt "github.com/vulcanize/ipld-eth-state-snapshot/fixture"
|
fixt "github.com/vulcanize/ipld-eth-state-snapshot/fixture"
|
||||||
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
|
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
|
||||||
@ -88,27 +89,23 @@ func TestPgCopy(t *testing.T) {
|
|||||||
pub := writeFiles(t, dir)
|
pub := writeFiles(t, dir)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
conn, err := pgx.Connect(ctx, pgConfig.DbConnectionString())
|
driver, err := postgres.NewSQLXDriver(ctx, pgConfig, nodeInfo)
|
||||||
test.NoError(t, err)
|
test.NoError(t, err)
|
||||||
|
db := postgres.NewPostgresDB(driver)
|
||||||
|
|
||||||
// clear existing test data
|
sql.TearDownDB(t, db)
|
||||||
pgDeleteTable := `DELETE FROM %s`
|
|
||||||
for _, tbl := range allTables {
|
|
||||||
_, err = conn.Exec(ctx, fmt.Sprintf(pgDeleteTable, tbl.Name))
|
|
||||||
test.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy from files
|
// copy from files
|
||||||
pgCopyStatement := `COPY %s FROM '%s' CSV`
|
pgCopyStatement := `COPY %s FROM '%s' CSV`
|
||||||
for _, tbl := range perBlockTables {
|
for _, tbl := range perBlockTables {
|
||||||
stm := fmt.Sprintf(pgCopyStatement, tbl.Name, TableFile(pub.dir, tbl.Name))
|
stm := fmt.Sprintf(pgCopyStatement, tbl.Name, TableFile(pub.dir, tbl.Name))
|
||||||
_, err = conn.Exec(ctx, stm)
|
_, err = db.Exec(ctx, stm)
|
||||||
test.NoError(t, err)
|
test.NoError(t, err)
|
||||||
}
|
}
|
||||||
for i := uint32(0); i < pub.txCounter; i++ {
|
for i := uint32(0); i < pub.txCounter; i++ {
|
||||||
for _, tbl := range perNodeTables {
|
for _, tbl := range perNodeTables {
|
||||||
stm := fmt.Sprintf(pgCopyStatement, tbl.Name, TableFile(pub.txDir(i), tbl.Name))
|
stm := fmt.Sprintf(pgCopyStatement, tbl.Name, TableFile(pub.txDir(i), tbl.Name))
|
||||||
_, err = conn.Exec(ctx, stm)
|
_, err = db.Exec(ctx, stm)
|
||||||
test.NoError(t, err)
|
test.NoError(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,7 +119,7 @@ func TestPgCopy(t *testing.T) {
|
|||||||
BlockHash string
|
BlockHash string
|
||||||
}
|
}
|
||||||
var header res
|
var header res
|
||||||
err = conn.QueryRow(ctx, pgQueryHeader, fixt.Block1_Header.Number.Uint64()).Scan(
|
err = db.QueryRow(ctx, pgQueryHeader, fixt.Block1_Header.Number.Uint64()).Scan(
|
||||||
&header.CID, &header.BlockHash)
|
&header.CID, &header.BlockHash)
|
||||||
test.NoError(t, err)
|
test.NoError(t, err)
|
||||||
|
|
||||||
|
61
pkg/snapshot/in_place_snapshot.go
Normal file
61
pkg/snapshot/in_place_snapshot.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright © 2022 Vulcanize, Inc
|
||||||
|
//
|
||||||
|
// 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 snapshot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
. "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
stateSnapShotPgStr = "SELECT state_snapshot($1, $2)"
|
||||||
|
storageSnapShotPgStr = "SELECT storage_snapshot($1, $2)"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InPlaceSnapshotParams struct {
|
||||||
|
StartHeight uint64
|
||||||
|
EndHeight uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateInPlaceSnapshot(config *Config, params InPlaceSnapshotParams) error {
|
||||||
|
db, err := sqlx.Connect("postgres", config.DB.ConnConfig.DbConnectionString())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
err = CommitOrRollback(tx, err)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("CommitOrRollback failed: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err = tx.Exec(stateSnapShotPgStr, params.StartHeight, params.EndHeight); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = tx.Exec(storageSnapShotPgStr, params.StartHeight, params.EndHeight); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
160
pkg/snapshot/in_place_snapshot_test.go
Normal file
160
pkg/snapshot/in_place_snapshot_test.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
// Copyright © 2022 Vulcanize, Inc
|
||||||
|
//
|
||||||
|
// 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 snapshot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql"
|
||||||
|
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
|
||||||
|
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
|
||||||
|
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
||||||
|
"github.com/multiformats/go-multihash"
|
||||||
|
|
||||||
|
fixt "github.com/vulcanize/ipld-eth-state-snapshot/fixture"
|
||||||
|
"github.com/vulcanize/ipld-eth-state-snapshot/pkg/snapshot/pg"
|
||||||
|
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
|
||||||
|
"github.com/vulcanize/ipld-eth-state-snapshot/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pgConfig = test.DefaultPgConfig
|
||||||
|
nodeInfo = test.DefaultNodeInfo
|
||||||
|
snapshotHeight = 4
|
||||||
|
|
||||||
|
allTables = []*snapt.Table{
|
||||||
|
&snapt.TableIPLDBlock,
|
||||||
|
&snapt.TableNodeInfo,
|
||||||
|
&snapt.TableHeader,
|
||||||
|
&snapt.TableStateNode,
|
||||||
|
&snapt.TableStorageNode,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeData(t *testing.T, db *postgres.DB) snapt.Publisher {
|
||||||
|
pub := pg.NewPublisher(db)
|
||||||
|
tx, err := pub.BeginTx()
|
||||||
|
test.NoError(t, err)
|
||||||
|
|
||||||
|
for _, block := range fixt.InPlaceSnapshotBlocks {
|
||||||
|
headerID := block.Hash.String()
|
||||||
|
|
||||||
|
for _, stateNode := range block.StateNodes {
|
||||||
|
test.NoError(t, pub.PublishStateNode(&stateNode, headerID, block.Number, tx))
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, stateStorageNodes := range block.StorageNodes {
|
||||||
|
stateNode := block.StateNodes[index]
|
||||||
|
|
||||||
|
for _, storageNode := range stateStorageNodes {
|
||||||
|
test.NoError(t, pub.PublishStorageNode(&storageNode, headerID, block.Number, stateNode.Path, tx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
test.NoError(t, tx.Commit())
|
||||||
|
|
||||||
|
test.NoError(t, pub.PublishHeader(&fixt.Block4_Header))
|
||||||
|
return pub
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateInPlaceSnapshot(t *testing.T) {
|
||||||
|
test.NeedsDB(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
driver, err := postgres.NewSQLXDriver(ctx, pgConfig, nodeInfo)
|
||||||
|
test.NoError(t, err)
|
||||||
|
db := postgres.NewPostgresDB(driver)
|
||||||
|
|
||||||
|
sql.TearDownDB(t, db)
|
||||||
|
|
||||||
|
_ = writeData(t, db)
|
||||||
|
|
||||||
|
params := InPlaceSnapshotParams{StartHeight: uint64(0), EndHeight: uint64(snapshotHeight)}
|
||||||
|
config := &Config{
|
||||||
|
Eth: &EthConfig{
|
||||||
|
NodeInfo: test.DefaultNodeInfo,
|
||||||
|
},
|
||||||
|
DB: &DBConfig{
|
||||||
|
URI: pgConfig.DbConnectionString(),
|
||||||
|
ConnConfig: pgConfig,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err = CreateInPlaceSnapshot(config, params)
|
||||||
|
test.NoError(t, err)
|
||||||
|
|
||||||
|
// Check inplace snapshot was created for state_cids
|
||||||
|
stateNodes := make([]models.StateNodeModel, 0)
|
||||||
|
pgQueryStateCids := `SELECT cast(state_cids.block_number AS TEXT), state_cids.cid, state_cids.state_leaf_key, state_cids.node_type, state_cids.state_path, state_cids.header_id, state_cids.mh_key
|
||||||
|
FROM eth.state_cids
|
||||||
|
WHERE eth.state_cids.block_number = $1
|
||||||
|
ORDER BY state_cids.state_path`
|
||||||
|
err = db.Select(ctx, &stateNodes, pgQueryStateCids, snapshotHeight)
|
||||||
|
test.NoError(t, err)
|
||||||
|
test.ExpectEqual(t, 4, len(stateNodes))
|
||||||
|
expectedStateNodes := fixt.ExpectedStateNodes
|
||||||
|
|
||||||
|
pgIpfsGet := `SELECT data FROM public.blocks
|
||||||
|
WHERE key = $1 AND block_number = $2`
|
||||||
|
|
||||||
|
for index, stateNode := range stateNodes {
|
||||||
|
var data []byte
|
||||||
|
err = db.Get(ctx, &data, pgIpfsGet, stateNode.MhKey, snapshotHeight)
|
||||||
|
test.NoError(t, err)
|
||||||
|
|
||||||
|
expectedStateNode := expectedStateNodes[index]
|
||||||
|
expectedCID, _ := ipld.RawdataToCid(ipld.MEthStateTrie, expectedStateNode.Value, multihash.KECCAK_256)
|
||||||
|
test.ExpectEqual(t, strconv.Itoa(snapshotHeight), stateNode.BlockNumber)
|
||||||
|
test.ExpectEqual(t, fixt.Block4_Header.Hash().String(), stateNode.HeaderID)
|
||||||
|
test.ExpectEqual(t, expectedCID.String(), stateNode.CID)
|
||||||
|
test.ExpectEqual(t, int(expectedStateNode.NodeType), stateNode.NodeType)
|
||||||
|
test.ExpectEqual(t, expectedStateNode.Key, common.HexToHash(stateNode.StateKey))
|
||||||
|
test.ExpectEqual(t, false, stateNode.Diff)
|
||||||
|
test.ExpectEqualBytes(t, expectedStateNode.Path, stateNode.Path)
|
||||||
|
test.ExpectEqualBytes(t, expectedStateNode.Value, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check inplace snapshot was created for storage_cids
|
||||||
|
storageNodes := make([]models.StorageNodeModel, 0)
|
||||||
|
pgQueryStorageCids := `SELECT cast(storage_cids.block_number AS TEXT), storage_cids.cid, storage_cids.state_path, storage_cids.storage_leaf_key, storage_cids.node_type, storage_cids.storage_path, storage_cids.mh_key, storage_cids.header_id
|
||||||
|
FROM eth.storage_cids
|
||||||
|
WHERE eth.storage_cids.block_number = $1
|
||||||
|
ORDER BY storage_cids.state_path, storage_cids.storage_path`
|
||||||
|
err = db.Select(ctx, &storageNodes, pgQueryStorageCids, snapshotHeight)
|
||||||
|
test.NoError(t, err)
|
||||||
|
|
||||||
|
for index, storageNode := range storageNodes {
|
||||||
|
expectedStorageNode := fixt.ExpectedStorageNodes[index]
|
||||||
|
expectedStorageCID, _ := ipld.RawdataToCid(ipld.MEthStorageTrie, expectedStorageNode.Value, multihash.KECCAK_256)
|
||||||
|
|
||||||
|
test.ExpectEqual(t, strconv.Itoa(snapshotHeight), storageNode.BlockNumber)
|
||||||
|
test.ExpectEqual(t, fixt.Block4_Header.Hash().String(), storageNode.HeaderID)
|
||||||
|
test.ExpectEqual(t, expectedStorageCID.String(), storageNode.CID)
|
||||||
|
test.ExpectEqual(t, int(expectedStorageNode.NodeType), storageNode.NodeType)
|
||||||
|
test.ExpectEqual(t, expectedStorageNode.Key, common.HexToHash(storageNode.StorageKey))
|
||||||
|
test.ExpectEqual(t, expectedStorageNode.StatePath, storageNode.StatePath)
|
||||||
|
test.ExpectEqual(t, expectedStorageNode.Path, storageNode.Path)
|
||||||
|
test.ExpectEqual(t, false, storageNode.Diff)
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
err = db.Get(ctx, &data, pgIpfsGet, storageNode.MhKey, snapshotHeight)
|
||||||
|
test.NoError(t, err)
|
||||||
|
test.ExpectEqualBytes(t, expectedStorageNode.Value, data)
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,7 @@ import (
|
|||||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||||
dshelp "github.com/ipfs/go-ipfs-ds-help"
|
dshelp "github.com/ipfs/go-ipfs-ds-help"
|
||||||
"github.com/multiformats/go-multihash"
|
"github.com/multiformats/go-multihash"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql"
|
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql"
|
||||||
@ -118,7 +119,12 @@ func (p *publisher) PublishHeader(header *types.Header) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tx := pubTx{snapTx, nil}
|
tx := pubTx{snapTx, nil}
|
||||||
defer func() { err = snapt.CommitOrRollback(tx, err) }()
|
defer func() {
|
||||||
|
err = snapt.CommitOrRollback(tx, err)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("CommitOrRollback failed: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if _, err = tx.publishIPLD(headerNode.Cid(), headerNode.RawData(), header.Number); err != nil {
|
if _, err = tx.publishIPLD(headerNode.Cid(), headerNode.RawData(), header.Number); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -2,12 +2,11 @@ package pg
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql"
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
|
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
|
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
|
||||||
"github.com/jackc/pgx/v4"
|
|
||||||
|
|
||||||
fixt "github.com/vulcanize/ipld-eth-state-snapshot/fixture"
|
fixt "github.com/vulcanize/ipld-eth-state-snapshot/fixture"
|
||||||
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
|
snapt "github.com/vulcanize/ipld-eth-state-snapshot/pkg/types"
|
||||||
@ -27,10 +26,8 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeData(t *testing.T) *publisher {
|
func writeData(t *testing.T, db *postgres.DB) *publisher {
|
||||||
driver, err := postgres.NewPGXDriver(context.Background(), pgConfig, nodeInfo)
|
pub := NewPublisher(db)
|
||||||
test.NoError(t, err)
|
|
||||||
pub := NewPublisher(postgres.NewPostgresDB(driver))
|
|
||||||
test.NoError(t, pub.PublishHeader(&fixt.Block1_Header))
|
test.NoError(t, pub.PublishHeader(&fixt.Block1_Header))
|
||||||
tx, err := pub.BeginTx()
|
tx, err := pub.BeginTx()
|
||||||
test.NoError(t, err)
|
test.NoError(t, err)
|
||||||
@ -47,17 +44,13 @@ func TestBasic(t *testing.T) {
|
|||||||
test.NeedsDB(t)
|
test.NeedsDB(t)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
conn, err := pgx.Connect(ctx, pgConfig.DbConnectionString())
|
driver, err := postgres.NewSQLXDriver(ctx, pgConfig, nodeInfo)
|
||||||
test.NoError(t, err)
|
test.NoError(t, err)
|
||||||
|
db := postgres.NewPostgresDB(driver)
|
||||||
|
|
||||||
// clear existing test data
|
sql.TearDownDB(t, db)
|
||||||
pgDeleteTable := `DELETE FROM %s`
|
|
||||||
for _, tbl := range allTables {
|
|
||||||
_, err = conn.Exec(ctx, fmt.Sprintf(pgDeleteTable, tbl.Name))
|
|
||||||
test.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = writeData(t)
|
_ = writeData(t, db)
|
||||||
|
|
||||||
// check header was successfully committed
|
// check header was successfully committed
|
||||||
pgQueryHeader := `SELECT cid, block_hash
|
pgQueryHeader := `SELECT cid, block_hash
|
||||||
@ -68,7 +61,7 @@ func TestBasic(t *testing.T) {
|
|||||||
BlockHash string
|
BlockHash string
|
||||||
}
|
}
|
||||||
var header res
|
var header res
|
||||||
err = conn.QueryRow(ctx, pgQueryHeader, fixt.Block1_Header.Number.Uint64()).Scan(
|
err = db.QueryRow(ctx, pgQueryHeader, fixt.Block1_Header.Number.Uint64()).Scan(
|
||||||
&header.CID, &header.BlockHash)
|
&header.CID, &header.BlockHash)
|
||||||
test.NoError(t, err)
|
test.NoError(t, err)
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
iter "github.com/vulcanize/go-eth-state-node-iterator"
|
iter "github.com/vulcanize/go-eth-state-node-iterator"
|
||||||
@ -203,7 +204,12 @@ func (s *Service) createSnapshot(it trie.NodeIterator, headerID string, height *
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() { err = CommitOrRollback(tx, err) }()
|
defer func() {
|
||||||
|
err = CommitOrRollback(tx, err)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("CommitOrRollback failed: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
for it.Next(true) {
|
for it.Next(true) {
|
||||||
res, err := resolveNode(it, s.stateDB.TrieDB())
|
res, err := resolveNode(it, s.stateDB.TrieDB())
|
||||||
|
@ -20,10 +20,10 @@ var (
|
|||||||
}
|
}
|
||||||
DefaultPgConfig = postgres.Config{
|
DefaultPgConfig = postgres.Config{
|
||||||
Hostname: "localhost",
|
Hostname: "localhost",
|
||||||
Port: 5432,
|
Port: 8077,
|
||||||
DatabaseName: "vulcanize_test",
|
DatabaseName: "vulcanize_testing",
|
||||||
Username: "vulcanize",
|
Username: "vdbm",
|
||||||
Password: "vulcanize_password",
|
Password: "password",
|
||||||
|
|
||||||
MaxIdle: 0,
|
MaxIdle: 0,
|
||||||
MaxConnLifetime: 0,
|
MaxConnLifetime: 0,
|
||||||
|
Loading…
Reference in New Issue
Block a user