Skip to content

Commit

Permalink
HIP-30: Balance migration (harmony-one#4499)
Browse files Browse the repository at this point in the history
* flags: set up preimage flags

* hip30: set up preimage import, export, api

* save pre-images by default

* add pre images api

* goimports

* commit rpc preimages file

* preimages: re-generate them using CLI

* add metrics and numbers for pre-images

* automate generation after import

* move from rpc to core

* goimports

* add back core/preimages.go file

* HIP-30: sharding configuration boilerplate

* update comments

* goimports

* HIP-30: minimum validator commission of 7%

Based on harmony-one#4495, which must be merged before this PR. This PR should be
rebased with dev after harmony-one#4495 is merged to retain atomicity of changes by
pull request.

* goimports

* HIP-30: Emission split implementation

Note that the allocated split of the emission goes directly to the
recipient (and not via the Reward). This is because rewards are indexed
by validator and not by delegator, and the recipient may/may not have
any delegations which we can reward. Even if one was guaranteed to
exist, it would mess up the math of the validator.

* set up mainnet recipient of emission split

* HIP-30: Emission split addresses for non mainnet

* HIP-30: deactivate shard 2 and 3 validators

* goimports

* update test

* goimports

* migrate balance uring epoch T - 1

highly untested code. also missing is the ability to generate a
pre-migration report for future verification.

* update test

* export prometheus metric when no error importing preimage

* add comment

* test account migration in localnet

* add preimages flags to rootflags

* enable preimages on the whitelist

* add the generate method

* fix cropping log

* fix cropping log

* cropping startpoint when bigger than endpoint

* add support for the rpcblocknumer type

* enable import api

* use earlies block

* debug logs

* debug logs

* debug logs

* debug logs

* fix error catching

* fix error catching

* make end optional for the comand line

* fix cropping logic

* improve error when apply message fails

* add balance on the error

* fix importing

* remove unused imports

---------

Co-authored-by: Nita Neou (Soph) <[email protected]>
Co-authored-by: Soph <[email protected]>
Co-authored-by: Diego Nava <[email protected]>
Co-authored-by: Diego Nava <[email protected]>
  • Loading branch information
5 people authored and Frozen committed Sep 19, 2023
1 parent 24afbcf commit 88b65e3
Show file tree
Hide file tree
Showing 10 changed files with 453 additions and 131 deletions.
104 changes: 17 additions & 87 deletions cmd/harmony/main.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package main

import (
"encoding/csv"
"fmt"
"io"
"io/ioutil"
"math/big"
"math/rand"
Expand Down Expand Up @@ -32,7 +30,6 @@ import (

ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/pkg/errors"
"github.com/spf13/cobra"
Expand All @@ -48,7 +45,6 @@ import (
"github.com/harmony-one/harmony/common/ntp"
"github.com/harmony-one/harmony/consensus"
"github.com/harmony-one/harmony/core"
"github.com/harmony-one/harmony/core/rawdb"
"github.com/harmony-one/harmony/hmy/downloader"
"github.com/harmony-one/harmony/internal/cli"
"github.com/harmony-one/harmony/internal/common"
Expand All @@ -64,7 +60,6 @@ import (
"github.com/harmony-one/harmony/p2p"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/webhooks"
prom "github.com/prometheus/client_golang/prometheus"
)

// Host
Expand Down Expand Up @@ -384,86 +379,13 @@ func setupNodeAndRun(hc harmonyconfig.HarmonyConfig) {
//// code to handle pre-image export, import and generation
if hc.Preimage != nil {
if hc.Preimage.ImportFrom != "" {
reader, err := os.Open(hc.Preimage.ImportFrom)
if err != nil {
fmt.Println("Could not open file for reading", err)
if err := core.ImportPreimages(
currentNode.Blockchain(),
hc.Preimage.ImportFrom,
); err != nil {
fmt.Println("Error importing", err)
os.Exit(1)
}
csvReader := csv.NewReader(reader)
chain := currentNode.Blockchain()
dbReader := chain.ChainDb()
imported := uint64(0)
for {
record, err := csvReader.Read()
if err == io.EOF {
fmt.Println("MyBlockNumber field missing, cannot proceed")
os.Exit(1)
}
if err != nil {
fmt.Println("Could not read from reader", err)
os.Exit(1)
}
// this means the address is a number
if blockNumber, err := strconv.ParseUint(record[1], 10, 64); err == nil {
if record[0] == "MyBlockNumber" {
// set this value in database, and prometheus, if needed
prev, err := rawdb.ReadPreimageImportBlock(dbReader)
if err != nil {
fmt.Println("No prior value found, overwriting")
}
if blockNumber > prev {
if rawdb.WritePreimageImportBlock(dbReader, blockNumber) != nil {
fmt.Println("Error saving last import block", err)
os.Exit(1)
}
// export blockNumber to prometheus
gauge := prom.NewGauge(
prom.GaugeOpts{
Namespace: "hmy",
Subsystem: "blockchain",
Name: "last_preimage_import",
Help: "the last known block for which preimages were imported",
},
)
prometheus.PromRegistry().MustRegister(
gauge,
)
gauge.Set(float64(blockNumber))
}
// this is the last record
imported = blockNumber
break
}
}
key := ethCommon.HexToHash(record[0])
value := ethCommon.Hex2Bytes(record[1])
// validate
if crypto.Keccak256Hash(value) != key {
fmt.Println("Data mismatch: skipping", record)
continue
}
// add to database
rawdb.WritePreimages(
dbReader, map[ethCommon.Hash][]byte{
key: value,
},
)
}
// now, at this point, we will have to generate missing pre-images
if imported != 0 {
genStart, _ := rawdb.ReadPreImageStartBlock(dbReader)
genEnd, _ := rawdb.ReadPreImageEndBlock(dbReader)
current := chain.CurrentBlock().NumberU64()
toGenStart, toGenEnd := core.FindMissingRange(imported, genStart, genEnd, current)
if toGenStart != 0 && toGenEnd != 0 {
if err := core.GeneratePreimages(
chain, toGenStart, toGenEnd,
); err != nil {
fmt.Println("Error generating", err)
os.Exit(1)
}
}
}
os.Exit(0)
} else if exportPath := hc.Preimage.ExportTo; exportPath != "" {
if err := core.ExportPreimages(
Expand All @@ -475,23 +397,31 @@ func setupNodeAndRun(hc harmonyconfig.HarmonyConfig) {
}
os.Exit(0)
// both must be set
} else if hc.Preimage.GenerateStart > 0 && hc.Preimage.GenerateEnd > 0 {
} else if hc.Preimage.GenerateStart > 0 {
chain := currentNode.Blockchain()
end := hc.Preimage.GenerateEnd
if number := chain.CurrentBlock().NumberU64(); number > end {
current := chain.CurrentBlock().NumberU64()
if end > current {
fmt.Printf(
"Cropping generate endpoint from %d to %d\n",
number, end,
end, current,
)
end = number
end = current
}

if end == 0 {
end = current
}

fmt.Println("Starting generation")
if err := core.GeneratePreimages(
chain,
hc.Preimage.GenerateStart, end,
); err != nil {
fmt.Println("Error generating", err)
os.Exit(1)
}
fmt.Println("Generation successful")
os.Exit(0)
}
os.Exit(0)
Expand Down
97 changes: 95 additions & 2 deletions core/preimages.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package core
import (
"encoding/csv"
"fmt"
"io"
"os"
"strconv"

ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/harmony-one/harmony/api/service/prometheus"
"github.com/harmony-one/harmony/core/rawdb"
"github.com/harmony-one/harmony/core/state"
Expand All @@ -14,6 +17,86 @@ import (
prom "github.com/prometheus/client_golang/prometheus"
)

// ImportPreimages is public so `main.go` can call it directly`
func ImportPreimages(chain BlockChain, path string) error {
reader, err := os.Open(path)
if err != nil {
return fmt.Errorf("could not open file for reading: %s", err)
}
csvReader := csv.NewReader(reader)
dbReader := chain.ChainDb()
imported := uint64(0)
for {
record, err := csvReader.Read()
if err == io.EOF {
return fmt.Errorf("MyBlockNumber field missing, cannot proceed")
}
if err != nil {
return fmt.Errorf("could not read from reader: %s", err)
}
// this means the address is a number
if blockNumber, err := strconv.ParseUint(record[1], 10, 64); err == nil {
if record[0] == "MyBlockNumber" {
// set this value in database, and prometheus, if needed
prev, err := rawdb.ReadPreimageImportBlock(dbReader)
if err != nil {
return fmt.Errorf("no prior value found, overwriting: %s", err)
}
if blockNumber > prev {
if rawdb.WritePreimageImportBlock(dbReader, blockNumber) != nil {
return fmt.Errorf("error saving last import block: %s", err)
}
// export blockNumber to prometheus
gauge := prom.NewGauge(
prom.GaugeOpts{
Namespace: "hmy",
Subsystem: "blockchain",
Name: "last_preimage_import",
Help: "the last known block for which preimages were imported",
},
)
prometheus.PromRegistry().MustRegister(
gauge,
)
gauge.Set(float64(blockNumber))
}
// this is the last record
imported = blockNumber
break
}
}
key := ethCommon.HexToHash(record[0])
value := ethCommon.Hex2Bytes(record[1])
// validate
if crypto.Keccak256Hash(value) != key {
fmt.Println("Data mismatch: skipping", record)
continue
}
// add to database
_ = rawdb.WritePreimages(
dbReader, map[ethCommon.Hash][]byte{
key: value,
},
)
}
// now, at this point, we will have to generate missing pre-images
if imported != 0 {
genStart, _ := rawdb.ReadPreImageStartBlock(dbReader)
genEnd, _ := rawdb.ReadPreImageEndBlock(dbReader)
current := chain.CurrentBlock().NumberU64()
toGenStart, toGenEnd := FindMissingRange(imported, genStart, genEnd, current)
if toGenStart != 0 && toGenEnd != 0 {
if err := GeneratePreimages(
chain, toGenStart, toGenEnd,
); err != nil {
return fmt.Errorf("error generating: %s", err)
}
}
}

return nil
}

// ExportPreimages is public so `main.go` can call it directly`
func ExportPreimages(chain BlockChain, path string) error {
// set up csv
Expand Down Expand Up @@ -102,6 +185,8 @@ func GeneratePreimages(chain BlockChain, start, end uint64) error {
if start < 2 {
return fmt.Errorf("too low starting point %d", start)
}
fmt.Println("generating from", start, "to", end)

// fetch all the blocks, from start and end both inclusive
// then execute them - the execution will write the pre-images
// to disk and we are good to go
Expand All @@ -111,15 +196,18 @@ func GeneratePreimages(chain BlockChain, start, end uint64) error {
var startingState *state.DB
var startingBlock *types.Block
for i := start - 1; i > 0; i-- {
fmt.Println("finding block number", i)
startingBlock = chain.GetBlockByNumber(i)
if startingBlock == nil {
fmt.Println("not found block number", i)
// rewound too much in snapdb, so exit loop
// although this is only designed for s2/s3 nodes in mind
// which do not have such a snapdb
break
}
fmt.Println("found block number", startingBlock.NumberU64(), startingBlock.Root().Hex())
state, err := chain.StateAt(startingBlock.Root())
if err == nil {
if err != nil {
continue
}
startingState = state
Expand All @@ -131,18 +219,23 @@ func GeneratePreimages(chain BlockChain, start, end uint64) error {

// now execute block T+1 based on starting state
for i := startingBlock.NumberU64() + 1; i <= end; i++ {
if i%100000 == 0 {
fmt.Println("processing block", i)
}
block := chain.GetBlockByNumber(i)
if block == nil {
// because we have startingBlock we must have all following
return fmt.Errorf("block %d not found", i)
}
_, _, _, _, _, _, endingState, err := chain.Processor().Process(block, startingState, *chain.GetVMConfig(), false)
if err == nil {
if err != nil {
return fmt.Errorf("error executing block #%d: %s", i, err)
}
startingState = endingState
}
// force any pre-images in memory so far to go to disk, if they haven't already
fmt.Println("committing images")

if err := chain.CommitPreimages(); err != nil {
return fmt.Errorf("error committing preimages %s", err)
}
Expand Down
Loading

0 comments on commit 88b65e3

Please sign in to comment.