github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/ledger/context.go (about)

     1  package ledger
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  
     8  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
     9  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger"
    10  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    11  )
    12  
    13  type ledgerContextKey string
    14  
    15  // ledgerContext is a struct to hold the context of a reconciliation (i.e., its
    16  // previous and next blocks and whether they are different)
    17  type ledgerContext struct {
    18  	PrevBlock base.Blknum
    19  	CurBlock  base.Blknum
    20  	NextBlock base.Blknum
    21  	ReconType types.ReconType
    22  }
    23  
    24  func newLedgerContext(prev, cur, next base.Blknum, isFirst, isLast, reversed bool) *ledgerContext {
    25  	_ = reversed // Silences unused parameter warning
    26  
    27  	if prev > cur || cur > next {
    28  		return &ledgerContext{
    29  			ReconType: types.Invalid,
    30  		}
    31  	}
    32  
    33  	reconType := types.Invalid
    34  	if cur == 0 {
    35  		reconType = types.Genesis
    36  	} else {
    37  		prevDiff := prev != cur
    38  		nextDiff := cur != next
    39  		if prevDiff && nextDiff {
    40  			reconType = types.DiffDiff
    41  		} else if !prevDiff && !nextDiff {
    42  			reconType = types.SameSame
    43  		} else if prevDiff {
    44  			reconType = types.DiffSame
    45  		} else if nextDiff {
    46  			reconType = types.SameDiff
    47  		} else {
    48  			reconType = types.Invalid
    49  			logger.Panic("should not happen")
    50  		}
    51  	}
    52  
    53  	if isFirst {
    54  		reconType |= types.First
    55  	}
    56  
    57  	if isLast {
    58  		reconType |= types.Last
    59  	}
    60  
    61  	return &ledgerContext{
    62  		PrevBlock: prev,
    63  		CurBlock:  cur,
    64  		NextBlock: next,
    65  		ReconType: reconType,
    66  		// Reversed:  reversed,
    67  	}
    68  }
    69  
    70  func (c *ledgerContext) Prev() base.Blknum {
    71  	return c.PrevBlock
    72  }
    73  
    74  func (c *ledgerContext) Cur() base.Blknum {
    75  	return c.CurBlock
    76  }
    77  
    78  func (c *ledgerContext) Next() base.Blknum {
    79  	return c.NextBlock
    80  }
    81  
    82  func (l *Ledger) ctxKey(bn base.Blknum, txid base.Txnum) ledgerContextKey {
    83  	// TODO: Is having the context per asset necessary? Can we use Locator?
    84  	// return fmt.Sprintf("%s-%09d-%05d", l.AccountFor.Hex(), bn, txid)
    85  	return ledgerContextKey(fmt.Sprintf("%09d-%05d", bn, txid))
    86  }
    87  
    88  const maxTestingBlock = 17000000
    89  
    90  // SetContexts visits the list of appearances and notes the block numbers of the next and previous
    91  // appearance's and if they are the same or different. Because balances are only available per block,
    92  // we must know this information to be able to calculate the correct post-tx balance.
    93  func (l *Ledger) SetContexts(chain string, apps []types.Appearance) error {
    94  	for i := 0; i < len(apps); i++ {
    95  		cur := base.Blknum(apps[i].BlockNumber)
    96  		prev := base.Blknum(apps[base.Max(1, i)-1].BlockNumber)
    97  		next := base.Blknum(apps[base.Min(i+1, len(apps)-1)].BlockNumber)
    98  		key := l.ctxKey(base.Blknum(apps[i].BlockNumber), base.Txnum(apps[i].TransactionIndex))
    99  		l.Contexts[key] = newLedgerContext(base.Blknum(prev), base.Blknum(cur), base.Blknum(next), i == 0, i == (len(apps)-1), l.Reversed)
   100  	}
   101  	l.debugContext()
   102  	return nil
   103  }
   104  
   105  func (l *Ledger) debugContext() {
   106  	if !l.TestMode {
   107  		return
   108  	}
   109  
   110  	keys := make([]ledgerContextKey, 0, len(l.Contexts))
   111  	for key := range l.Contexts {
   112  		keys = append(keys, key)
   113  	}
   114  
   115  	sort.Slice(keys, func(i, j int) bool {
   116  		return string(keys[i]) < string(keys[j])
   117  	})
   118  
   119  	logger.Info(strings.Repeat("-", 60))
   120  	logger.Info(fmt.Sprintf("Contexts (%d)", len(keys)))
   121  	for _, key := range keys {
   122  		c := l.Contexts[key]
   123  		if c.CurBlock > maxTestingBlock {
   124  			continue
   125  		}
   126  		msg := ""
   127  		rr := c.ReconType &^ (types.First | types.Last)
   128  		switch rr {
   129  		case types.Genesis:
   130  			msg = fmt.Sprintf(" %s", c.ReconType.String()+"-diff")
   131  		case types.DiffDiff:
   132  			msg = fmt.Sprintf(" %s", c.ReconType.String())
   133  		case types.SameSame:
   134  			msg = fmt.Sprintf(" %s", c.ReconType.String())
   135  		case types.DiffSame:
   136  			msg = fmt.Sprintf(" %s", c.ReconType.String())
   137  		case types.SameDiff:
   138  			msg = fmt.Sprintf(" %s", c.ReconType.String())
   139  		default:
   140  			msg = fmt.Sprintf(" %s should not happen!", c.ReconType.String())
   141  		}
   142  		logger.Info(fmt.Sprintf("%s: % 10d % 10d % 11d%s", key, c.PrevBlock, c.CurBlock, c.NextBlock, msg))
   143  	}
   144  }