mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 19:50:36 +00:00
293 lines
8.4 KiB
Go
293 lines
8.4 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"time"
|
||
|
|
||
|
"github.com/ledgerwatch/turbo-geth/common"
|
||
|
"github.com/ledgerwatch/turbo-geth/common/dbutils"
|
||
|
"github.com/ledgerwatch/turbo-geth/common/hexutil"
|
||
|
"github.com/ledgerwatch/turbo-geth/consensus/ethash"
|
||
|
"github.com/ledgerwatch/turbo-geth/core"
|
||
|
"github.com/ledgerwatch/turbo-geth/core/state"
|
||
|
"github.com/ledgerwatch/turbo-geth/core/types/accounts"
|
||
|
"github.com/ledgerwatch/turbo-geth/core/vm"
|
||
|
"github.com/ledgerwatch/turbo-geth/crypto"
|
||
|
"github.com/ledgerwatch/turbo-geth/ethdb"
|
||
|
"github.com/ledgerwatch/turbo-geth/params"
|
||
|
"github.com/ledgerwatch/turbo-geth/rlp"
|
||
|
"github.com/ledgerwatch/turbo-geth/trie"
|
||
|
)
|
||
|
|
||
|
type EthGetProof struct {
|
||
|
CommonResponse
|
||
|
Result AccountResult `json:"result"`
|
||
|
}
|
||
|
|
||
|
// Result structs for GetProof
|
||
|
type AccountResult struct {
|
||
|
Address common.Address `json:"address"`
|
||
|
AccountProof []string `json:"accountProof"`
|
||
|
Balance *hexutil.Big `json:"balance"`
|
||
|
CodeHash common.Hash `json:"codeHash"`
|
||
|
Nonce hexutil.Uint64 `json:"nonce"`
|
||
|
StorageHash common.Hash `json:"storageHash"`
|
||
|
StorageProof []StorageResult `json:"storageProof"`
|
||
|
}
|
||
|
type StorageResult struct {
|
||
|
Key string `json:"key"`
|
||
|
Value *hexutil.Big `json:"value"`
|
||
|
Proof []string `json:"proof"`
|
||
|
}
|
||
|
|
||
|
func proofs(chaindata string, url string, block int) {
|
||
|
fileName := "trie.txt"
|
||
|
ethDb, err := ethdb.NewBoltDatabase(chaindata)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
defer ethDb.Close()
|
||
|
var t *trie.Trie
|
||
|
if _, err = os.Stat(fileName); err != nil {
|
||
|
if os.IsNotExist(err) {
|
||
|
// Resolve 6 top levels of the accounts trie
|
||
|
r := trie.NewResolver(6, true, uint64(block))
|
||
|
t = trie.New(common.Hash{})
|
||
|
req := t.NewResolveRequest(nil, []byte{}, 0, nil)
|
||
|
r.AddRequest(req)
|
||
|
err = r.ResolveWithDb(ethDb, uint64(block))
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
fmt.Printf("Resolved with hash: %x\n", t.Hash())
|
||
|
f, err1 := os.Create(fileName)
|
||
|
if err1 == nil {
|
||
|
defer f.Close()
|
||
|
t.Print(f)
|
||
|
} else {
|
||
|
panic(err1)
|
||
|
}
|
||
|
fmt.Printf("Saved trie to file\n")
|
||
|
} else {
|
||
|
panic(err)
|
||
|
}
|
||
|
} else {
|
||
|
f, err1 := os.Open(fileName)
|
||
|
if err1 == nil {
|
||
|
defer f.Close()
|
||
|
var err2 error
|
||
|
t, err2 = trie.Load(f)
|
||
|
if err2 != nil {
|
||
|
panic(err2)
|
||
|
}
|
||
|
fmt.Printf("Restored from file with hash: %x\n", t.Hash())
|
||
|
} else {
|
||
|
panic(err1)
|
||
|
}
|
||
|
}
|
||
|
var client = &http.Client{
|
||
|
Timeout: time.Second * 600,
|
||
|
}
|
||
|
reqID := 0
|
||
|
|
||
|
level := 0
|
||
|
diffKeys := [][]byte{{}}
|
||
|
var newDiffKeys [][]byte
|
||
|
|
||
|
for len(diffKeys) > 0 && level < 6 {
|
||
|
|
||
|
fmt.Printf("================================================\n")
|
||
|
fmt.Printf("LEVEL %d, diffKeys: %d\n", level, len(diffKeys))
|
||
|
fmt.Printf("================================================\n")
|
||
|
for _, diffKey := range diffKeys {
|
||
|
// Find account with the suitable hash
|
||
|
var startKey common.Hash
|
||
|
for i, d := range diffKey {
|
||
|
if i%2 == 0 {
|
||
|
startKey[i/2] |= (d << 4)
|
||
|
} else {
|
||
|
startKey[i/2] |= d
|
||
|
}
|
||
|
}
|
||
|
var account common.Address
|
||
|
var found bool
|
||
|
err = ethDb.Walk(dbutils.PreimagePrefix, startKey[:], uint(4*len(diffKey)), func(k, v []byte) (bool, error) {
|
||
|
if len(v) == common.AddressLength {
|
||
|
copy(account[:], v)
|
||
|
found = true
|
||
|
return false, nil
|
||
|
}
|
||
|
return true, nil
|
||
|
})
|
||
|
if err != nil {
|
||
|
fmt.Printf("Error when looking for suitable account for diffKey %x\n", diffKey)
|
||
|
return
|
||
|
}
|
||
|
if !found {
|
||
|
fmt.Printf("Could not find suitable account for diffKey %x\n", diffKey)
|
||
|
return
|
||
|
}
|
||
|
reqID++
|
||
|
template := `{"jsonrpc":"2.0","method":"eth_getProof","params":["0x%x",[],"0x%x"],"id":%d}`
|
||
|
var proof EthGetProof
|
||
|
if err := post(client, url, fmt.Sprintf(template, account, block, reqID), &proof); err != nil {
|
||
|
fmt.Printf("Could not get block number: %v\n", err)
|
||
|
return
|
||
|
}
|
||
|
if proof.Error != nil {
|
||
|
fmt.Printf("Error retrieving proof: %d %s\n", proof.Error.Code, proof.Error.Message)
|
||
|
return
|
||
|
}
|
||
|
if len(proof.Result.AccountProof) <= len(diffKey) {
|
||
|
fmt.Printf("RPC result needs to be at least %d levels deep\n", len(diffKey)+1)
|
||
|
return
|
||
|
}
|
||
|
p := proof.Result.AccountProof[len(diffKey)]
|
||
|
b := common.FromHex(p)
|
||
|
h := common.BytesToHash(crypto.Keccak256(b))
|
||
|
hE, err := t.HashOfHexKey(diffKey)
|
||
|
if err != nil {
|
||
|
fmt.Printf("Error computing partial hash for %x: %v\n", diffKey, err)
|
||
|
return
|
||
|
}
|
||
|
if h != hE {
|
||
|
fmt.Printf("key %x: %x %x adding nibbles: ", diffKey, h, hE)
|
||
|
var branch [17][]byte
|
||
|
err = rlp.DecodeBytes(b, &branch)
|
||
|
if err != nil {
|
||
|
fmt.Printf("Error decoding: %v\n", err)
|
||
|
fmt.Printf("%s\n", p)
|
||
|
}
|
||
|
// Expand keys further
|
||
|
for nibble := byte(0); nibble < 16; nibble++ {
|
||
|
newDiff := make([]byte, len(diffKey)+1)
|
||
|
copy(newDiff, diffKey)
|
||
|
newDiff[len(diffKey)] = nibble
|
||
|
var proofHash common.Hash
|
||
|
copy(proofHash[:], branch[nibble])
|
||
|
if proofHash != (common.Hash{}) || err != nil {
|
||
|
newHash, err := t.HashOfHexKey(newDiff)
|
||
|
if err != nil {
|
||
|
fmt.Printf("Error computing partial hash for %x: %v\n", newDiff, err)
|
||
|
}
|
||
|
if proofHash != newHash {
|
||
|
newDiffKeys = append(newDiffKeys, newDiff)
|
||
|
fmt.Printf("%x", nibble)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
fmt.Printf("\n")
|
||
|
} else {
|
||
|
fmt.Printf("MATCH key %x: %x %x\n", diffKey, h, hE)
|
||
|
}
|
||
|
}
|
||
|
diffKeys = newDiffKeys
|
||
|
newDiffKeys = nil
|
||
|
level++
|
||
|
}
|
||
|
fmt.Printf("\n\nRESULT:\n")
|
||
|
for _, diffKey := range diffKeys {
|
||
|
fmt.Printf("%x\n", diffKey)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func fixState(chaindata string, url string) {
|
||
|
stateDb, err := ethdb.NewBoltDatabase(chaindata)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
defer stateDb.Close()
|
||
|
engine := ethash.NewFullFaker()
|
||
|
chainConfig := params.MainnetChainConfig
|
||
|
bc, err := core.NewBlockChain(stateDb, nil, chainConfig, engine, vm.Config{}, nil)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
currentBlock := bc.CurrentBlock()
|
||
|
blockNum := currentBlock.NumberU64()
|
||
|
blockHash := currentBlock.Hash()
|
||
|
fmt.Printf("Block number: %d\n", blockNum)
|
||
|
fmt.Printf("Block root hash: %x\n", currentBlock.Root())
|
||
|
reqID := 0
|
||
|
roots := make(map[common.Hash]*accounts.Account)
|
||
|
var client = &http.Client{
|
||
|
Timeout: time.Second * 600,
|
||
|
}
|
||
|
|
||
|
err = stateDb.Walk(dbutils.StorageBucket, nil, 0, func(k, v []byte) (bool, error) {
|
||
|
var addrHash common.Hash
|
||
|
copy(addrHash[:], k[:32])
|
||
|
if _, ok := roots[addrHash]; !ok {
|
||
|
if enc, _ := stateDb.Get(dbutils.AccountsBucket, addrHash[:]); enc == nil {
|
||
|
roots[addrHash] = nil
|
||
|
} else {
|
||
|
var account accounts.Account
|
||
|
if err = account.DecodeForStorage(enc); err != nil {
|
||
|
return false, err
|
||
|
}
|
||
|
roots[addrHash] = &account
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true, nil
|
||
|
})
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
for addrHash, account := range roots {
|
||
|
if account != nil && account.Root != trie.EmptyRoot {
|
||
|
st := trie.New(account.Root)
|
||
|
sr := trie.NewResolver(32, false, blockNum)
|
||
|
key := []byte{}
|
||
|
contractPrefix := make([]byte, common.HashLength+state.IncarnationLength)
|
||
|
copy(contractPrefix, addrHash[:])
|
||
|
binary.BigEndian.PutUint64(contractPrefix[common.HashLength:], account.Incarnation^^uint64(0))
|
||
|
streq := st.NewResolveRequest(contractPrefix, key, 0, account.Root[:])
|
||
|
sr.AddRequest(streq)
|
||
|
err = sr.ResolveWithDb(stateDb, blockNum)
|
||
|
if err != nil {
|
||
|
fmt.Printf("%x: %v\n", addrHash, err)
|
||
|
address, _ := stateDb.Get(dbutils.PreimagePrefix, addrHash[:])
|
||
|
template := `{"jsonrpc":"2.0","method":"debug_storageRangeAt","params":["0x%x", %d,"0x%x","0x%x",%d],"id":%d}`
|
||
|
sm := make(map[common.Hash]storageEntry)
|
||
|
nextKey := &common.Hash{}
|
||
|
for nextKey != nil {
|
||
|
reqID++
|
||
|
var sr DebugStorageRange
|
||
|
if err = post(client, url, fmt.Sprintf(template, blockHash, 0, address, *nextKey, 1024, reqID), &sr); err != nil {
|
||
|
fmt.Printf("Could not get storageRange: %v\n", err)
|
||
|
return
|
||
|
}
|
||
|
if sr.Error != nil {
|
||
|
fmt.Printf("Error getting storageRange: %d %s\n", sr.Error.Code, sr.Error.Message)
|
||
|
break
|
||
|
} else {
|
||
|
nextKey = sr.Result.NextKey
|
||
|
for k, v := range sr.Result.Storage {
|
||
|
sm[k] = v
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for key, entry := range sm {
|
||
|
var cKey [common.HashLength + state.IncarnationLength + common.HashLength]byte
|
||
|
copy(cKey[:], addrHash[:])
|
||
|
binary.BigEndian.PutUint64(cKey[common.HashLength:], account.Incarnation^^uint64(0))
|
||
|
copy(cKey[common.HashLength+state.IncarnationLength:], key[:])
|
||
|
dbValue, _ := stateDb.Get(dbutils.StorageBucket, cKey[:])
|
||
|
value := bytes.TrimLeft(entry.Value[:], "\x00")
|
||
|
if !bytes.Equal(dbValue, value) {
|
||
|
fmt.Printf("Key: %x, value: %x, dbValue: %x\n", key, value, dbValue)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
}
|