diff --git a/miner.go b/miner.go index 91395ae..85ad705 100644 --- a/miner.go +++ b/miner.go @@ -55,9 +55,10 @@ retry: }}, } - if cs.Index.Height >= cs.Network.HardforkV2.AllowHeight { + childHeight := cs.Index.Height + 1 + if childHeight >= cs.Network.HardforkV2.AllowHeight { b.V2 = &types.V2BlockData{ - Height: cs.Index.Height + 1, + Height: childHeight, } } @@ -69,14 +70,14 @@ retry: b.Transactions = append(b.Transactions, txn) b.MinerPayouts[0].Value = b.MinerPayouts[0].Value.Add(txn.TotalFees()) } - for _, txn := range v2Txns { - if weight += cs.V2TransactionWeight(txn); weight > cs.MaxBlockWeight() { - break - } - b.V2.Transactions = append(b.V2.Transactions, txn) - b.MinerPayouts[0].Value = b.MinerPayouts[0].Value.Add(txn.MinerFee) - } if b.V2 != nil { + for _, txn := range v2Txns { + if weight += cs.V2TransactionWeight(txn); weight > cs.MaxBlockWeight() { + break + } + b.V2.Transactions = append(b.V2.Transactions, txn) + b.MinerPayouts[0].Value = b.MinerPayouts[0].Value.Add(txn.MinerFee) + } b.V2.Commitment = cs.Commitment(cs.TransactionsCommitment(b.Transactions, b.V2Transactions()), addr) } found := FindBlockNonce(cs, &b, timeout) diff --git a/miner_test.go b/miner_test.go index 2bb4c73..8f5b47c 100644 --- a/miner_test.go +++ b/miner_test.go @@ -1,16 +1,17 @@ -package coreutils +package coreutils_test import ( "testing" "time" "go.sia.tech/core/types" + "go.sia.tech/coreutils" "go.sia.tech/coreutils/chain" + "go.sia.tech/coreutils/testutil" ) func TestMiner(t *testing.T) { - n, genesisBlock := chain.TestnetZen() - n.InitialTarget = types.BlockID{0xFF} + n, genesisBlock := testutil.Network() sk := types.GeneratePrivateKey() genesisBlock.Transactions = []types.Transaction{{ @@ -59,7 +60,7 @@ func TestMiner(t *testing.T) { } // assert the minerpayout includes the txn fee - b, found := MineBlock(cm, types.VoidAddress, time.Second) + b, found := coreutils.MineBlock(cm, types.VoidAddress, time.Second) if !found { t.Fatal("PoW failed") } else if len(b.MinerPayouts) != 1 { @@ -68,3 +69,86 @@ func TestMiner(t *testing.T) { t.Fatal("unexpected miner payout", b.MinerPayouts[0].Value.ExactString()) } } + +func TestV2MineBlocks(t *testing.T) { + n, genesisBlock := testutil.V2Network() + n.HardforkV2.AllowHeight = 5 + n.HardforkV2.RequireHeight = 10 + n.InitialTarget = types.BlockID{0xFF} + + genesisBlock.Transactions = []types.Transaction{{ + SiacoinOutputs: []types.SiacoinOutput{ + { + Address: types.AnyoneCanSpend().Address(), + Value: types.Siacoins(10), + }, + }, + }} + + store, tipState, err := chain.NewDBStore(chain.NewMemDB(), n, genesisBlock) + if err != nil { + t.Fatal(err) + } + cm := chain.NewManager(store, tipState) + + mineBlocks := func(t *testing.T, n int) { + for ; n > 0; n-- { + b, ok := coreutils.MineBlock(cm, types.VoidAddress, time.Second) + if !ok { + t.Fatal("failed to mine block") + } else if err := cm.AddBlocks([]types.Block{b}); err != nil { + t.Fatal(err) + } + } + } + + // mine until just before the allow height + mineBlocks(t, 4) + + elements := make(map[types.Hash256]types.SiacoinElement) + _, applied, err := cm.UpdatesSince(types.ChainIndex{}, 500) + if err != nil { + t.Fatal(err) + } + for _, cau := range applied { + cau.ForEachSiacoinElement(func(sce types.SiacoinElement, created, spent bool) { + switch { + case created && spent: + return + case sce.SiacoinOutput.Address != types.AnyoneCanSpend().Address(): + return + case created: + elements[sce.ID] = sce + case spent: + delete(elements, sce.ID) + } + }) + + for k, v := range elements { + cau.UpdateElementProof(&v.StateElement) + elements[k] = v + } + } + + var se types.SiacoinElement + for _, v := range elements { + se = v + break + } + + txn := types.V2Transaction{ + MinerFee: se.SiacoinOutput.Value, + SiacoinInputs: []types.V2SiacoinInput{{ + Parent: se, + SatisfiedPolicy: types.SatisfiedPolicy{Policy: types.AnyoneCanSpend()}, + }}, + } + + // add the transaction to the pool + _, err = cm.AddV2PoolTransactions(cm.Tip(), []types.V2Transaction{txn}) + if err != nil { + t.Fatal(err) + } + + mineBlocks(t, 1) +}