func TestPreprocessesGenesis(t *testing.T) {
	testDbInstances(t, true, testPreprocessesGenesis)
}
TestPreprocessesGenesis tests that preprocessing the chain works when the
underlying chain is still at the genesis block.
package backend
import (
	"context"
	"strconv"
	"testing"
	"time"
	"decred.org/dcrros/backend/backenddb"
	"decred.org/dcrros/backend/internal/memdb"
	rtypes "github.com/coinbase/rosetta-sdk-go/types"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/chaincfg/v3"
	"github.com/decred/dcrd/wire"
	"github.com/stretchr/testify/require"
)
func accountBalanceByHeight(t *testing.T, svr *Server, height int64,
	account string) int64 {
	t.Helper()
	req := &rtypes.AccountBalanceRequest{
		BlockIdentifier: &rtypes.PartialBlockIdentifier{
			Index: &height,
		},
		AccountIdentifier: &rtypes.AccountIdentifier{
			Address: account,
			Metadata: map[string]interface{}{
				"script_version": uint16(0),
			},
		},
	}
	res, rerr := svr.AccountBalance(testCtx(t), req)
	require.Nil(t, rerr)
	gotBalance, err := strconv.ParseInt(res.Balances[0].Value, 10, 64)
	require.NoError(t, err)
	return gotBalance
}
func accountBalanceByHash(t *testing.T, svr *Server, hash chainhash.Hash,
	account string) int64 {
	t.Helper()
	bh := hash.String()
	req := &rtypes.AccountBalanceRequest{
		BlockIdentifier: &rtypes.PartialBlockIdentifier{
			Hash: &bh,
		},
		AccountIdentifier: &rtypes.AccountIdentifier{
			Address: account,
			Metadata: map[string]interface{}{
				"script_version": uint16(0),
			},
		},
	}
	res, rerr := svr.AccountBalance(testCtx(t), req)
	require.Nil(t, rerr)
	gotBalance, err := strconv.ParseInt(res.Balances[0].Value, 10, 64)
	require.NoError(t, err)
	return gotBalance
}
func accountBalanceAtTip(mc *mockChain, svr *Server, account string) int64 {
	mc.t.Helper()
	req := &rtypes.AccountBalanceRequest{
		AccountIdentifier: &rtypes.AccountIdentifier{
			Address: account,
			Metadata: map[string]interface{}{
				"script_version": uint16(0),
			},
		},
	}
	res, rerr := svr.AccountBalance(testCtx(mc.t), req)
	require.Nil(mc.t, rerr)
	if mc.tipHeight != res.BlockIdentifier.Index {
		mc.t.Fatalf("block index returned by AccountBalance() does not "+
			"match chain tip. want=%d got=%d", mc.tipHeight,
			res.BlockIdentifier.Index)
	}
	if mc.tipHash.String() != res.BlockIdentifier.Hash {
		mc.t.Fatalf("block hash returned by AccountBalance() does not " +
			"match chain tip.")
	}
	gotBalance, err := strconv.ParseInt(res.Balances[0].Value, 10, 64)
	require.NoError(mc.t, err)
	return gotBalance
}
func assertBalanceAtTip(mc *mockChain, svr *Server, account string, wantBalance int64) {
	mc.t.Helper()
	gotBalance := accountBalanceAtTip(mc, svr, account)
	if wantBalance != gotBalance {
		mc.t.Fatalf("unexpected balance. want=%d got=%d", wantBalance, gotBalance)
	}
}
func testPreprocessesAccounts(t *testing.T, db backenddb.DB) {
	params := chaincfg.RegNetParams()
	c := newMockChain(t, params)
	pks := mustHex("76a91464098ee97e822d8b98e93608c4f0cdbb559146fa88ac")
	addr := "RsHTkwUoDYKh3SJCj1FSoWbKy1P2DA6DHA5"
	disapprove := func(b *wire.MsgBlock) {
		b.Header.VoteBits = 0
	}
	manglers := func(ms ...blockMangler) []blockMangler {
		return ms
	}
	toAddr := func(v, nb int64) blockMangler {
		return c.txToAddr(pks, v, nb)
	}
	fromAddr := func(v, nb int64) blockMangler {
		return c.txFromAddr(pks, v, nb)
	}
	testCases := []struct {
		name        string
		manglers    []blockMangler
		wantBalance int64
	}{{
		name:        "block before addr receives anything",
		manglers:    manglers(),
		wantBalance: 0,
	}, {
		name:        "single output added to address",
		manglers:    manglers(toAddr(10, 1)),
		wantBalance: 10,
	}, {
		name:        "second block without txs to addr",
		manglers:    manglers(),
		wantBalance: 10,
	}, {
		name:        "second block with single output added to address",
		manglers:    manglers(toAddr(10, 1)),
		wantBalance: 20,
	}, {
		name:        "multiple txs adding to addr",
		manglers:    manglers(toAddr(10, 1), toAddr(10, 1)),
		wantBalance: 40,
	}, {
		name:        "single tx sending multiple times to same addr",
		manglers:    manglers(toAddr(10, 3)),
		wantBalance: 70,
	}, {
		name:        "spend a sigle utxo in the block",
		manglers:    manglers(fromAddr(10, 1)),
		wantBalance: 60,
	}, {
		name:        "spend multiple utxos in the same tx",
		manglers:    manglers(fromAddr(10, 2)),
		wantBalance: 40,
	}, {
		name:        "spend multiple utxo in different txs",
		manglers:    manglers(fromAddr(10, 1), fromAddr(10, 1)),
		wantBalance: 20,
	}, {
		name:        "add and spend in the same block",
		manglers:    manglers(toAddr(20, 1), fromAddr(10, 1)),
		wantBalance: 30,
	}, {
		name:        "add to addr in a block that will be disapproved",
		manglers:    manglers(toAddr(10, 1)),
		wantBalance: 40,
	}, {
		name:        "disapprove the block that added to the addr",
		manglers:    manglers(disapprove),
		wantBalance: 30,
	}, {
		name:        "spend in a block that will be disapproved",
		manglers:    manglers(fromAddr(10, 1)),
		wantBalance: 20,
	}, {
		name:        "disapprove the block that spent from the addr",
		manglers:    manglers(disapprove),
		wantBalance: 30,
	}, {
		name:        "add and spend in a block that will be disapproved",
		manglers:    manglers(toAddr(20, 1), fromAddr(10, 1)),
		wantBalance: 40,
	}, {
		name:        "disapprove previous but also add in this block",
		manglers:    manglers(disapprove, toAddr(20, 1)),
		wantBalance: 50,
	}, {
		name:        "final block",
		manglers:    manglers(),
		wantBalance: 50,
	}}
	for _, tc := range testCases {
		c.extendTip(tc.manglers...)
	}
	cfg := &ServerConfig{
		ChainParams: params,
		DBType:      dbTypePreconfigured,
		c:           c,
		db:          db,
	}
	svr := newTestServer(t, cfg)
	err := svr.preProcessAccounts(testCtx(t))
	require.NoError(t, err)
	for i, tc := range testCases {
		height := int64(i + 1)
		gotBalance := accountBalanceByHeight(t, svr, height, addr)
		if gotBalance != tc.wantBalance {
			t.Fatalf("'%s': unexpected balance. want=%d got=%d",
				tc.name, tc.wantBalance, gotBalance)
		}
		bh := c.blocksByHeight[height]
		gotBalance = accountBalanceByHash(t, svr, *bh, addr)
		if gotBalance != tc.wantBalance {
			t.Fatalf("'%s': unexpected balance by hash. want=%d got=%d",
				tc.name, tc.wantBalance, gotBalance)
		}
	}
	gotBalance := accountBalanceAtTip(c, svr, addr)
	wantBalance := testCases[len(testCases)-1].wantBalance
	if gotBalance != wantBalance {
		t.Fatalf("unexpected balance at tip. want=%d got=%d",
			wantBalance, gotBalance)
	}
}
func TestPreprocessesAccounts(t *testing.T) {
	testDbInstances(t, true, testPreprocessesAccounts)
}
func testPreprocessesGenesis(t *testing.T, db backenddb.DB) {
	params := chaincfg.RegNetParams()
	c := newMockChain(t, params)
	cfg := &ServerConfig{
		ChainParams: params,
		DBType:      dbTypePreconfigured,
		c:           c,
		db:          db,
	}
	svr := newTestServer(t, cfg)
	err := svr.preProcessAccounts(testCtx(t))
	require.NoError(t, err)
}