github.com/0xsequence/ethkit@v1.25.0/ethmonitor/bootstrap.go (about)

     1  package ethmonitor
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  
     7  	"github.com/0xsequence/ethkit/go-ethereum/core/types"
     8  )
     9  
    10  // BootstrapFromBlocks will bootstrap the ethmonitor canonical chain from input blocks,
    11  // while also verifying the chain hashes link together.
    12  func (c *Chain) BootstrapFromBlocks(blocks []*Block) error {
    13  	return c.bootstrapBlocks(blocks)
    14  }
    15  
    16  // BootstrapFromBlocksJSON is convenience method which accepts json and bootstraps
    17  // the ethmonitor chain. This method is here mostly for debugging purposes and recommend
    18  // that you use BootstrapFromBlocks and handle constructing block events outside of ethmonitor.
    19  func (c *Chain) BootstrapFromBlocksJSON(data []byte) error {
    20  	var blocks Blocks
    21  	err := json.Unmarshal(data, &blocks)
    22  	if err != nil {
    23  		return fmt.Errorf("ethmonitor: BootstrapFromBlocksJSON failed to unmarshal: %w", err)
    24  	}
    25  	return c.bootstrapBlocks(blocks)
    26  }
    27  
    28  func (c *Chain) bootstrapBlocks(blocks Blocks) error {
    29  	if !c.bootstrapMode {
    30  		return fmt.Errorf("ethmonitor: monitor must be in Bootstrap mode to use bootstrap methods")
    31  	}
    32  	if c.blocks != nil {
    33  		return fmt.Errorf("ethmonitor: chain has already been bootstrapped")
    34  	}
    35  
    36  	if len(blocks) == 0 {
    37  		c.blocks = make(Blocks, 0, c.retentionLimit)
    38  		return nil
    39  	}
    40  
    41  	if len(blocks) == 1 {
    42  		c.blocks = blocks.Copy()
    43  		return nil
    44  	}
    45  
    46  	c.blocks = make(Blocks, 0, c.retentionLimit)
    47  
    48  	if len(blocks) > c.retentionLimit {
    49  		blocks = blocks[len(blocks)-c.retentionLimit-1:]
    50  	}
    51  
    52  	for _, b := range blocks {
    53  		if b.Event == Added {
    54  			err := c.push(b)
    55  			if err != nil {
    56  				return fmt.Errorf("ethmonitor: bootstrap failed to build canonical chain: %w", err)
    57  			}
    58  		} else {
    59  			c.pop()
    60  		}
    61  	}
    62  
    63  	return nil
    64  }
    65  
    66  func (c *Chain) Snapshot() ([]byte, error) {
    67  	c.mu.Lock()
    68  	defer c.mu.Unlock()
    69  
    70  	data, err := json.Marshal(c.blocks)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return data, nil
    76  }
    77  
    78  type blockSnapshot struct {
    79  	Block *types.Block `json:"block"`
    80  	Event Event        `json:"event"`
    81  	Logs  []types.Log  `json:"logs"`
    82  	OK    bool         `json:"ok"`
    83  }
    84  
    85  func (b *Block) MarshalJSON() ([]byte, error) {
    86  	return json.Marshal(&blockSnapshot{
    87  		Block: b.Block,
    88  		Event: b.Event,
    89  		Logs:  b.Logs,
    90  		OK:    b.OK,
    91  	})
    92  }
    93  
    94  func (b *Block) UnmarshalJSON(data []byte) error {
    95  	var s *blockSnapshot
    96  	err := json.Unmarshal(data, &s)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	b.Block = s.Block
   101  	b.Event = s.Event
   102  	b.Logs = s.Logs
   103  	b.OK = s.OK
   104  	return nil
   105  }