From 0d3a1652885144fff5741df7dadb984d6c795ef5 Mon Sep 17 00:00:00 2001
From: Liuhaai <haixiang@iotex.io>
Date: Tue, 14 Feb 2023 10:46:13 -0800
Subject: [PATCH 1/3] fix test and speedup validation

---
 action/protocol/context.go             |   4 +-
 blockchain/integrity/benchmark_test.go |   1 -
 blockchain/integrity/integrity_test.go | 101 +++++++++++++++++++++++--
 state/factory/workingset.go            |  59 ++++++++++++++-
 testutil/timestamp_test.go             |   2 +-
 5 files changed, 153 insertions(+), 14 deletions(-)

diff --git a/action/protocol/context.go b/action/protocol/context.go
index 4d724cce9a..85ef6084b6 100644
--- a/action/protocol/context.go
+++ b/action/protocol/context.go
@@ -110,7 +110,7 @@ type (
 		CorrectGasRefund                        bool
 		FixRewardErroCheckPosition              bool
 		EnableWeb3Rewarding                     bool
-		EnableNodeInfo                          bool
+		SkipSystemActionNonce                   bool
 	}
 
 	// FeatureWithHeightCtx provides feature check functions.
@@ -247,7 +247,7 @@ func WithFeatureCtx(ctx context.Context) context.Context {
 			CorrectGasRefund:                        g.IsOkhotsk(height),
 			FixRewardErroCheckPosition:              g.IsOkhotsk(height),
 			EnableWeb3Rewarding:                     g.IsPalau(height),
-			EnableNodeInfo:                          g.IsPalau(height),
+			SkipSystemActionNonce:                   g.IsPalau(height),
 		},
 	)
 }
diff --git a/blockchain/integrity/benchmark_test.go b/blockchain/integrity/benchmark_test.go
index fb98b24a6e..6bc972d072 100644
--- a/blockchain/integrity/benchmark_test.go
+++ b/blockchain/integrity/benchmark_test.go
@@ -289,7 +289,6 @@ func newChainInDB() (blockchain.Blockchain, actpool.ActPool, error) {
 	var genesisNonce uint64 = 0
 
 	// make a transfer from genesisAccount to a and b,because stateTX cannot store data in height 0
-	genesisNonce++
 	tsf, err := action.SignedTransfer(userA.String(), genesisPriKey, genesisNonce, big.NewInt(1e17), []byte{}, testutil.TestGasLimit, big.NewInt(testutil.TestGasPriceInt64))
 	if err != nil {
 		return nil, nil, err
diff --git a/blockchain/integrity/integrity_test.go b/blockchain/integrity/integrity_test.go
index 1678e5e5da..77ce5a42a1 100644
--- a/blockchain/integrity/integrity_test.go
+++ b/blockchain/integrity/integrity_test.go
@@ -1466,9 +1466,6 @@ func TestBlockchain_AccountState(t *testing.T) {
 	require := require.New(t)
 
 	cfg := config.Default
-	// disable account-based testing
-	// create chain
-	cfg.Genesis.InitBalanceMap[identityset.Address(0).String()] = "100"
 	ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis)
 	registry := protocol.NewRegistry()
 	acc := account.NewProtocol(rewarding.DepositGas)
@@ -1480,14 +1477,104 @@ func TestBlockchain_AccountState(t *testing.T) {
 	require.NoError(err)
 	dao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf})
 	bc := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao, factory.NewMinter(sf, ap))
-	require.NoError(bc.Start(context.Background()))
+	require.NoError(bc.Start(ctx))
 	require.NotNil(bc)
+	defer func() {
+		require.NoError(bc.Stop(ctx))
+	}()
 	s, err := accountutil.AccountState(ctx, sf, identityset.Address(0))
 	require.NoError(err)
 	require.Equal(uint64(1), s.PendingNonce())
-	require.Equal(big.NewInt(100), s.Balance)
-	require.Equal(hash.ZeroHash256, s.Root)
-	require.Equal([]byte(nil), s.CodeHash)
+	require.Equal(unit.ConvertIotxToRau(100000000), s.Balance)
+	require.Zero(s.Root)
+	require.Nil(s.CodeHash)
+}
+
+func TestNewAccountAction(t *testing.T) {
+	require := require.New(t)
+
+	cfg := config.Default
+	cfg.Genesis.OkhotskBlockHeight = 1
+	ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis)
+	registry := protocol.NewRegistry()
+	acc := account.NewProtocol(rewarding.DepositGas)
+	require.NoError(acc.Register(registry))
+	factoryCfg := factory.GenerateConfig(cfg.Chain, cfg.Genesis)
+	sf, err := factory.NewFactory(factoryCfg, db.NewMemKVStore(), factory.RegistryOption(registry))
+	require.NoError(err)
+	ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool)
+	require.NoError(err)
+	dao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf})
+	bc := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao, factory.NewMinter(sf, ap))
+	require.NoError(bc.Start(ctx))
+	require.NotNil(bc)
+	defer func() {
+		require.NoError(bc.Stop(ctx))
+	}()
+
+	// create a new address, transfer 4 IOTX
+	newSk, err := iotexcrypto.HexStringToPrivateKey("55499c1b09f687488af9e4ee9e2bd53c7c8c3ddc69d4d9345a04b13030cffabe")
+	require.NoError(err)
+	newAddr := newSk.PublicKey().Address()
+	tx, err := action.SignedTransfer(newAddr.String(), identityset.PrivateKey(0), 1, big.NewInt(4*unit.Iotx), nil, testutil.TestGasLimit, testutil.TestGasPrice)
+	require.NoError(err)
+	require.NoError(ap.Add(ctx, tx))
+	blk, err := bc.MintNewBlock(testutil.TimestampNow())
+	require.NoError(err)
+	require.NoError(bc.CommitBlock(blk))
+	ap.Reset()
+
+	// initiate transfer from new address
+	tx, err = action.SignedTransfer(identityset.Address(0).String(), newSk, 0, big.NewInt(unit.Iotx), nil, testutil.TestGasLimit, testutil.TestGasPrice)
+	require.NoError(err)
+	require.NoError(ap.Add(ctx, tx))
+	tx1, err := action.SignedTransfer(identityset.Address(1).String(), newSk, 1, big.NewInt(unit.Iotx), nil, testutil.TestGasLimit, testutil.TestGasPrice)
+	require.NoError(err)
+	require.NoError(ap.Add(ctx, tx1))
+	blk1, err := bc.MintNewBlock(testutil.TimestampNow())
+	require.NoError(err)
+	require.NoError(bc.CommitBlock(blk1))
+	ap.Reset()
+
+	// commit 2 blocks into a new chain
+	for _, validateNonce := range []bool{false, true} {
+		if validateNonce {
+			cfg.Genesis.PalauBlockHeight = 2
+		} else {
+			cfg.Genesis.PalauBlockHeight = 20
+		}
+		ctx = genesis.WithGenesisContext(context.Background(), cfg.Genesis)
+		factoryCfg = factory.GenerateConfig(cfg.Chain, cfg.Genesis)
+		sf1, err := factory.NewFactory(factoryCfg, db.NewMemKVStore(), factory.RegistryOption(registry))
+		require.NoError(err)
+		dao1 := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf1})
+		bc1 := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao1, factory.NewMinter(sf1, ap))
+		require.NoError(bc1.Start(ctx))
+		require.NotNil(bc1)
+		defer func() {
+			require.NoError(bc1.Stop(ctx))
+		}()
+		require.NoError(bc1.CommitBlock(blk))
+		err = bc1.CommitBlock(blk1)
+		if validateNonce {
+			require.NoError(err)
+		} else {
+			require.Equal(action.ErrNonceTooHigh, errors.Cause(err))
+		}
+
+		// verify new addr
+		s, err := accountutil.AccountState(ctx, sf1, newAddr)
+		require.NoError(err)
+		if validateNonce {
+			require.EqualValues(2, s.PendingNonce())
+			require.Equal(big.NewInt(2*unit.Iotx), s.Balance)
+		} else {
+			require.Zero(s.PendingNonce())
+			require.Equal(big.NewInt(4*unit.Iotx), s.Balance)
+		}
+		require.Zero(s.Root)
+		require.Nil(s.CodeHash)
+	}
 }
 
 func TestBlocks(t *testing.T) {
diff --git a/state/factory/workingset.go b/state/factory/workingset.go
index b47f3cee0d..538f7e147a 100644
--- a/state/factory/workingset.go
+++ b/state/factory/workingset.go
@@ -352,7 +352,54 @@ func (ws *workingSet) validateNonce(ctx context.Context, blk *block.Block) error
 			if nonce != pendingNonce+uint64(i) {
 				return errors.Wrapf(
 					action.ErrNonceTooHigh,
-					"the %d nonce %d of address %s (init pending nonce %d) is not continuously increasing",
+					"the %d-th nonce %d of address %s (init pending nonce %d) is not continuously increasing",
+					i,
+					nonce,
+					srcAddr,
+					pendingNonce,
+				)
+			}
+		}
+	}
+	return nil
+}
+
+func (ws *workingSet) validateNonce2(ctx context.Context, blk *block.Block) error {
+	accountNonceMap := make(map[string][]uint64)
+	for _, selp := range blk.Actions {
+		if action.IsSystemAction(selp) {
+			continue
+		}
+
+		caller := selp.SenderAddress()
+		if caller == nil {
+			return errors.New("failed to get address")
+		}
+		srcAddr := caller.String()
+		if _, ok := accountNonceMap[srcAddr]; !ok {
+			accountNonceMap[srcAddr] = make([]uint64, 0)
+		}
+		accountNonceMap[srcAddr] = append(accountNonceMap[srcAddr], selp.Nonce())
+	}
+
+	// Special handling for genesis block
+	if blk.Height() == 0 {
+		return nil
+	}
+	// Verify each account's Nonce
+	for srcAddr, receivedNonces := range accountNonceMap {
+		addr, _ := address.FromString(srcAddr)
+		confirmedState, err := accountutil.AccountState(ctx, ws, addr)
+		if err != nil {
+			return errors.Wrapf(err, "failed to get the confirmed nonce of address %s", srcAddr)
+		}
+		sort.Slice(receivedNonces, func(i, j int) bool { return receivedNonces[i] < receivedNonces[j] })
+		pendingNonce := confirmedState.PendingNonce()
+		for i, nonce := range receivedNonces {
+			if nonce != pendingNonce+uint64(i) {
+				return errors.Wrapf(
+					action.ErrNonceTooHigh,
+					"the %d-th nonce %d of address %s (init pending nonce %d) is not continuously increasing",
 					i,
 					nonce,
 					srcAddr,
@@ -517,8 +564,14 @@ func updateReceiptIndex(receipts []*action.Receipt) {
 }
 
 func (ws *workingSet) ValidateBlock(ctx context.Context, blk *block.Block) error {
-	if err := ws.validateNonce(ctx, blk); err != nil {
-		return errors.Wrap(err, "failed to validate nonce")
+	if protocol.MustGetFeatureCtx(ctx).SkipSystemActionNonce {
+		if err := ws.validateNonce2(ctx, blk); err != nil {
+			return errors.Wrap(err, "failed to validate nonce")
+		}
+	} else {
+		if err := ws.validateNonce(ctx, blk); err != nil {
+			return errors.Wrap(err, "failed to validate nonce")
+		}
 	}
 	if err := ws.process(ctx, blk.RunnableActions().Actions()); err != nil {
 		log.L().Error("Failed to update state.", zap.Uint64("height", ws.height), zap.Error(err))
diff --git a/testutil/timestamp_test.go b/testutil/timestamp_test.go
index 4a471cc1ee..7f84b0518a 100644
--- a/testutil/timestamp_test.go
+++ b/testutil/timestamp_test.go
@@ -15,5 +15,5 @@ import (
 func TestTimestamp(t *testing.T) {
 	assert := assert.New(t)
 	time1 := TimestampNow()
-	assert.True(time.Now().After(time1))
+	assert.False(time.Now().Before(time1))
 }

From 270b507a6f1faf0af4fe55b0bb8c9129911084bf Mon Sep 17 00:00:00 2001
From: Dustin Xie <dahuaxie@gmail.com>
Date: Thu, 6 Apr 2023 11:04:08 -0700
Subject: [PATCH 2/3] checkNonceContinuity()

---
 state/factory/workingset.go | 29 +++++------------------------
 1 file changed, 5 insertions(+), 24 deletions(-)

diff --git a/state/factory/workingset.go b/state/factory/workingset.go
index 538f7e147a..273103e6a8 100644
--- a/state/factory/workingset.go
+++ b/state/factory/workingset.go
@@ -338,30 +338,7 @@ func (ws *workingSet) validateNonce(ctx context.Context, blk *block.Block) error
 	if blk.Height() == 0 {
 		return nil
 	}
-	// Verify each account's Nonce
-	for srcAddr, receivedNonces := range accountNonceMap {
-		addr, _ := address.FromString(srcAddr)
-		confirmedState, err := accountutil.AccountState(ctx, ws, addr)
-		if err != nil {
-			return errors.Wrapf(err, "failed to get the confirmed nonce of address %s", srcAddr)
-		}
-		receivedNonces := receivedNonces
-		sort.Slice(receivedNonces, func(i, j int) bool { return receivedNonces[i] < receivedNonces[j] })
-		pendingNonce := confirmedState.PendingNonce()
-		for i, nonce := range receivedNonces {
-			if nonce != pendingNonce+uint64(i) {
-				return errors.Wrapf(
-					action.ErrNonceTooHigh,
-					"the %d-th nonce %d of address %s (init pending nonce %d) is not continuously increasing",
-					i,
-					nonce,
-					srcAddr,
-					pendingNonce,
-				)
-			}
-		}
-	}
-	return nil
+	return ws.checkNonceContinuity(ctx, accountNonceMap)
 }
 
 func (ws *workingSet) validateNonce2(ctx context.Context, blk *block.Block) error {
@@ -386,6 +363,10 @@ func (ws *workingSet) validateNonce2(ctx context.Context, blk *block.Block) erro
 	if blk.Height() == 0 {
 		return nil
 	}
+	return ws.checkNonceContinuity(ctx, accountNonceMap)
+}
+
+func (ws *workingSet) checkNonceContinuity(ctx context.Context, accountNonceMap map[string][]uint64) error {
 	// Verify each account's Nonce
 	for srcAddr, receivedNonces := range accountNonceMap {
 		addr, _ := address.FromString(srcAddr)

From 0594a622940cfd0a118a04485c6e489b6b9766f6 Mon Sep 17 00:00:00 2001
From: Dustin Xie <dahuaxie@gmail.com>
Date: Sun, 9 Apr 2023 18:25:46 -0700
Subject: [PATCH 3/3] special handling for genesis block not needed

---
 state/factory/workingset.go | 14 ++------------
 1 file changed, 2 insertions(+), 12 deletions(-)

diff --git a/state/factory/workingset.go b/state/factory/workingset.go
index 273103e6a8..4ef6ad264a 100644
--- a/state/factory/workingset.go
+++ b/state/factory/workingset.go
@@ -333,15 +333,10 @@ func (ws *workingSet) validateNonce(ctx context.Context, blk *block.Block) error
 		}
 		appendActionIndex(accountNonceMap, caller.String(), selp.Nonce())
 	}
-
-	// Special handling for genesis block
-	if blk.Height() == 0 {
-		return nil
-	}
 	return ws.checkNonceContinuity(ctx, accountNonceMap)
 }
 
-func (ws *workingSet) validateNonce2(ctx context.Context, blk *block.Block) error {
+func (ws *workingSet) validateNonceSkipSystemAction(ctx context.Context, blk *block.Block) error {
 	accountNonceMap := make(map[string][]uint64)
 	for _, selp := range blk.Actions {
 		if action.IsSystemAction(selp) {
@@ -358,11 +353,6 @@ func (ws *workingSet) validateNonce2(ctx context.Context, blk *block.Block) erro
 		}
 		accountNonceMap[srcAddr] = append(accountNonceMap[srcAddr], selp.Nonce())
 	}
-
-	// Special handling for genesis block
-	if blk.Height() == 0 {
-		return nil
-	}
 	return ws.checkNonceContinuity(ctx, accountNonceMap)
 }
 
@@ -546,7 +536,7 @@ func updateReceiptIndex(receipts []*action.Receipt) {
 
 func (ws *workingSet) ValidateBlock(ctx context.Context, blk *block.Block) error {
 	if protocol.MustGetFeatureCtx(ctx).SkipSystemActionNonce {
-		if err := ws.validateNonce2(ctx, blk); err != nil {
+		if err := ws.validateNonceSkipSystemAction(ctx, blk); err != nil {
 			return errors.Wrap(err, "failed to validate nonce")
 		}
 	} else {