moonstream/crawlers/ldb/cmd/actions.go

178 wiersze
4.7 KiB
Go

package cmd
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
_ "github.com/lib/pq"
)
var (
// Block which not found in database or have inconsistencies with blockchain
corruptBlocks CorruptBlocks
)
// Write down inconsistent state between database and blockchain
/*
- number (uint64): Block number
- source (string): Source of nonconformity [blockchain, database]
- description (string): Description of error, why block marked as malformed
*/
func (cb *CorruptBlocks) registerCorruptBlock(number uint64, source, description string) {
cb.Blocks = append(cb.Blocks, CorruptBlock{
Number: number,
Source: source,
Description: description,
})
}
// Add new blocks with transactions to database
func add(blockchain string, blockNumbers []uint64) error {
for _, bn := range blockNumbers {
block, err := localConnections.getChainBlock(bn)
if err != nil {
description := fmt.Sprintf("Unable to get block: %d from chain, err %v", bn, err)
fmt.Println(description)
corruptBlocks.registerCorruptBlock(bn, "blockchain", description)
continue
}
td := localConnections.Chain.GetTd(block.Hash(), block.NumberU64())
chainTxs := localConnections.getChainTxs(block.Hash(), bn)
err = localConnections.writeDatabaseBlockTxs(blockchain, block, chainTxs, td)
if err != nil {
fmt.Printf("Error occurred due saving block %d with transactions in database: %v", bn, err)
}
fmt.Printf("Processed block number: %d\r", bn)
}
return nil
}
// Return range of block hashes with transaction hashes from blockchain
func show(blockNumbers []uint64) error {
for _, bn := range blockNumbers {
block, err := localConnections.getChainBlock(bn)
if err != nil {
fmt.Printf("Unable to get block: %d from chain, err %v\n", bn, err)
continue
}
chainTxs := localConnections.getChainTxs(block.Hash(), bn)
var txs []common.Hash
for _, tx := range chainTxs {
txs = append(txs, tx.Hash())
}
fmt.Printf("Block %d block with hash: %s and transactions: %s\n", block.Number(), block.Hash().String(), txs)
}
return nil
}
// Run verification flow of blockchain with database data
func verify(blockchain string, blockNumbers []uint64, workers int) error {
jobsCh := make(chan Job, workers)
resultCh := make(chan Result, len(blockNumbers))
doneCh := make(chan struct{}, workers)
// Add jobs
go func() {
for _, bn := range blockNumbers {
jobsCh <- Job{
BlockNumber: bn,
Results: resultCh,
}
}
close(jobsCh)
}()
for i := 0; i < workers; i++ {
// Do jobs
go func() {
for job := range jobsCh {
chainBlock, err := localConnections.getChainBlock(job.BlockNumber)
if err != nil {
job.Results <- Result{
ErrorOutput: fmt.Sprintf("Unable to get block: %d from chain, err %v", job.BlockNumber, err),
ErrorSource: "blockchain",
Number: job.BlockNumber,
}
continue
}
dbBlock, err := localConnections.getDatabaseBlockTxs(blockchain, chainBlock.Hash().String())
if err != nil {
job.Results <- Result{
ErrorOutput: fmt.Sprintf("Unable to get block: %d, err: %v", job.BlockNumber, err),
ErrorSource: "database",
Number: job.BlockNumber,
}
continue
}
if dbBlock.Number == nil {
job.Results <- Result{
ErrorOutput: fmt.Sprintf("Block %d not presented in database", job.BlockNumber),
ErrorSource: "database",
Number: job.BlockNumber,
}
continue
}
if chainBlock.NumberU64() != dbBlock.Number.Uint64() {
job.Results <- Result{
ErrorOutput: fmt.Sprintf("Incorrect %d block retrieved from database", job.BlockNumber),
ErrorSource: "database",
Number: job.BlockNumber,
}
continue
}
chainTxs := localConnections.getChainTxs(chainBlock.Hash(), job.BlockNumber)
if len(chainTxs) != len(dbBlock.Transactions) {
job.Results <- Result{
ErrorOutput: fmt.Sprintf("Different number of transactions in block %d, err %v", job.BlockNumber, err),
ErrorSource: "database",
Number: job.BlockNumber,
}
continue
}
job.Results <- Result{
Output: fmt.Sprintf("Processed block number: %d", job.BlockNumber),
}
}
doneCh <- struct{}{}
}()
}
// Await completion
go func() {
for i := 0; i < workers; i++ {
<-doneCh
}
close(resultCh)
}()
for result := range resultCh {
if result.ErrorOutput != "" {
fmt.Println(result.ErrorOutput)
corruptBlocks.registerCorruptBlock(result.Number, result.ErrorSource, result.ErrorOutput)
}
if result.Output != "" {
fmt.Println(result.Output)
}
if result.Output != "" && result.ErrorOutput != "" {
fmt.Printf("Unprocessable result with error: %s and output: %s", result.ErrorOutput, result.Output)
}
}
return nil
}