diff --git a/action/envelope.go b/action/envelope.go index 978abbac91..99c45938f8 100644 --- a/action/envelope.go +++ b/action/envelope.go @@ -50,7 +50,6 @@ type ( Nonce() uint64 Gas() uint64 GasPrice() *big.Int - EffectiveGasPrice(*big.Int) *big.Int TxDynamicGas AccessList() types.AccessList TxBlob @@ -71,6 +70,7 @@ type ( TxDynamicGas interface { GasTipCap() *big.Int GasFeeCap() *big.Int + EffectiveGasPrice(*big.Int) *big.Int } TxBlob interface { diff --git a/action/protocol/execution/evm/evm_test.go b/action/protocol/execution/evm/evm_test.go index 97499c295d..a6275ef5bf 100644 --- a/action/protocol/execution/evm/evm_test.go +++ b/action/protocol/execution/evm/evm_test.go @@ -260,12 +260,12 @@ func TestConstantinople(t *testing.T) { }, { "io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y", - 41174200, + 33730920, }, // after Vanuatu { action.EmptyAddress, - 41174201, + 33730921, }, { "io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y", diff --git a/action/protocol/protocol.go b/action/protocol/protocol.go index b7888d383c..425ce17502 100644 --- a/action/protocol/protocol.go +++ b/action/protocol/protocol.go @@ -149,7 +149,7 @@ func SplitGas(ctx context.Context, tx action.TxDynamicGas, usedGas uint64) (*big return priority.Mul(priority, gas), base.Mul(base, gas), nil } -func EffectiveGasPrice(ctx context.Context, tx action.TxCommon) *big.Int { +func EffectiveGasPrice(ctx context.Context, tx action.TxDynamicGas) *big.Int { if !MustGetFeatureCtx(ctx).EnableDynamicFeeTx { return nil } diff --git a/action/protocol/rewarding/protocol.go b/action/protocol/rewarding/protocol.go index 01a3397bd5..bc404f6b45 100644 --- a/action/protocol/rewarding/protocol.go +++ b/action/protocol/rewarding/protocol.go @@ -387,7 +387,7 @@ func (p *Protocol) deleteStateV2(sm protocol.StateManager, key []byte) error { func (p *Protocol) settleSystemAction( ctx context.Context, sm protocol.StateManager, - act action.TxCommon, + act action.TxDynamicGas, status uint64, si int, logs []*action.Log, @@ -399,7 +399,7 @@ func (p *Protocol) settleSystemAction( func (p *Protocol) settleUserAction( ctx context.Context, sm protocol.StateManager, - act action.TxCommon, + act action.TxDynamicGas, status uint64, si int, logs []*action.Log, @@ -411,7 +411,7 @@ func (p *Protocol) settleUserAction( func (p *Protocol) settleAction( ctx context.Context, sm protocol.StateManager, - act action.TxCommon, + act action.TxDynamicGas, status uint64, si int, isSystemAction bool, diff --git a/action/protocol/staking/protocol.go b/action/protocol/staking/protocol.go index 953b66d286..0932a79065 100644 --- a/action/protocol/staking/protocol.go +++ b/action/protocol/staking/protocol.go @@ -689,7 +689,7 @@ const ( func (p *Protocol) settleAction( ctx context.Context, sm protocol.StateManager, - act action.TxCommon, + act action.TxDynamicGas, status uint64, logs []*action.Log, tLogs []*action.TransactionLog, diff --git a/action/sealedenvelope.go b/action/sealedenvelope.go index a6cd2ef64d..9a1581bd71 100644 --- a/action/sealedenvelope.go +++ b/action/sealedenvelope.go @@ -242,3 +242,13 @@ func (sealed *SealedEnvelope) VerifySignature() error { } return nil } + +// Protected says whether the transaction is replay-protected. +func (sealed *SealedEnvelope) Protected() bool { + switch sealed.encoding { + case iotextypes.Encoding_TX_CONTAINER: + return sealed.Envelope.(*txContainer).tx.Protected() + default: + return sealed.encoding != iotextypes.Encoding_ETHEREUM_UNPROTECTED + } +} diff --git a/actpool/actpool.go b/actpool/actpool.go index 91354c9c05..5716435a2b 100644 --- a/actpool/actpool.go +++ b/actpool/actpool.go @@ -82,6 +82,7 @@ type ActPool interface { ReceiveBlock(*block.Block) error AddActionEnvelopeValidators(...action.SealedEnvelopeValidator) + AddSubscriber(sub Subscriber) } // Subscriber is the interface for actpool subscriber diff --git a/actsync/actionsync.go b/actsync/actionsync.go index a7e6b42e3e..269db6fa5d 100644 --- a/actsync/actionsync.go +++ b/actsync/actionsync.go @@ -95,7 +95,6 @@ func (as *ActionSync) Stop(ctx context.Context) error { return err } close(as.quit) - close(as.syncChan) as.wg.Wait() return nil } @@ -127,28 +126,33 @@ func (as *ActionSync) ReceiveAction(_ context.Context, hash hash.Hash256) { func (as *ActionSync) sync() { defer as.wg.Done() - for hash := range as.syncChan { - log.L().Debug("syncing action", log.Hex("hash", hash[:])) - channelFullnessMtc.WithLabelValues("action").Set(float64(len(as.syncChan)) / float64(cap(as.syncChan))) - ctx, cancel := context.WithTimeout(context.Background(), unicaseTimeout) - defer cancel() - msg, ok := as.actions.Load(hash) - if !ok { - log.L().Debug("action not requested or already received", log.Hex("hash", hash[:])) - continue - } - if time.Since(msg.(*actionMsg).lastTime) < as.cfg.Interval { - log.L().Debug("action is recently requested", log.Hex("hash", hash[:])) - continue - } - msg.(*actionMsg).lastTime = time.Now() - // TODO: enhancement, request multiple actions in one message - if err := as.requestFromNeighbors(ctx, hash); err != nil { - log.L().Warn("Failed to request action from neighbors", zap.Error(err)) - counterMtc.WithLabelValues("failed").Inc() + for { + select { + case hash := <-as.syncChan: + log.L().Debug("syncing action", log.Hex("hash", hash[:])) + channelFullnessMtc.WithLabelValues("action").Set(float64(len(as.syncChan)) / float64(cap(as.syncChan))) + ctx, cancel := context.WithTimeout(context.Background(), unicaseTimeout) + defer cancel() + msg, ok := as.actions.Load(hash) + if !ok { + log.L().Debug("action not requested or already received", log.Hex("hash", hash[:])) + continue + } + if time.Since(msg.(*actionMsg).lastTime) < as.cfg.Interval { + log.L().Debug("action is recently requested", log.Hex("hash", hash[:])) + continue + } + msg.(*actionMsg).lastTime = time.Now() + // TODO: enhancement, request multiple actions in one message + if err := as.requestFromNeighbors(ctx, hash); err != nil { + log.L().Warn("Failed to request action from neighbors", zap.Error(err)) + counterMtc.WithLabelValues("failed").Inc() + } + case <-as.quit: + log.L().Info("quitting action sync") + return } } - log.L().Info("quitting action sync") } func (as *ActionSync) triggerSync() { diff --git a/actsync/actionsync_test.go b/actsync/actionsync_test.go index aff97f4136..640387250d 100644 --- a/actsync/actionsync_test.go +++ b/actsync/actionsync_test.go @@ -126,4 +126,40 @@ func TestActionSync(t *testing.T) { r.False(ok, "action should be removed after received") } }) + t.Run("requestWhenStopping", func(t *testing.T) { + count := atomic.Int32{} + as := NewActionSync(Config{ + Size: 1000, + Interval: 10 * time.Millisecond, + }, &Helper{ + P2PNeighbor: func() ([]peer.AddrInfo, error) { + return neighbors, nil + }, + UnicastOutbound: func(_ context.Context, p peer.AddrInfo, msg proto.Message) error { + count.Add(1) + return nil + }, + }) + r.NoError(as.Start(context.Background())) + acts := []hash.Hash256{} + for i := 0; i < 100; i++ { + acts = append(acts, hash.Hash256b([]byte{byte(i)})) + } + wg := sync.WaitGroup{} + for i := 0; i < 10; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + for k := 0; k <= 10; k++ { + idx := i*10 + k + if idx >= len(acts) { + break + } + as.RequestAction(context.Background(), acts[idx]) + } + }(i) + } + r.NoError(as.Stop(context.Background())) + wg.Wait() + }) } diff --git a/api/action_radio.go b/api/action_radio.go new file mode 100644 index 0000000000..6beda0ea06 --- /dev/null +++ b/api/action_radio.go @@ -0,0 +1,93 @@ +package api + +import ( + "context" + "encoding/hex" + + "github.com/iotexproject/iotex-proto/golang/iotextypes" + "go.uber.org/zap" + "google.golang.org/protobuf/proto" + + "github.com/iotexproject/iotex-core/v2/action" + "github.com/iotexproject/iotex-core/v2/pkg/log" + batch "github.com/iotexproject/iotex-core/v2/pkg/messagebatcher" +) + +// ActionRadioOption is the option to create ActionRadio +type ActionRadioOption func(*ActionRadio) + +// WithMessageBatch enables message batching +func WithMessageBatch() ActionRadioOption { + return func(ar *ActionRadio) { + ar.messageBatcher = batch.NewManager(func(msg *batch.Message) error { + return ar.broadcastHandler(context.Background(), ar.chainID, msg.Data) + }) + } +} + +// ActionRadio broadcasts actions to the network +type ActionRadio struct { + broadcastHandler BroadcastOutbound + messageBatcher *batch.Manager + chainID uint32 +} + +// NewActionRadio creates a new ActionRadio +func NewActionRadio(broadcastHandler BroadcastOutbound, chainID uint32, opts ...ActionRadioOption) *ActionRadio { + ar := &ActionRadio{ + broadcastHandler: broadcastHandler, + chainID: chainID, + } + for _, opt := range opts { + opt(ar) + } + return ar +} + +// Start starts the action radio +func (ar *ActionRadio) Start() error { + if ar.messageBatcher != nil { + return ar.messageBatcher.Start() + } + return nil +} + +// Stop stops the action radio +func (ar *ActionRadio) Stop() error { + if ar.messageBatcher != nil { + return ar.messageBatcher.Stop() + } + return nil +} + +// OnAdded broadcasts the action to the network +func (ar *ActionRadio) OnAdded(selp *action.SealedEnvelope) { + var ( + hasSidecar = selp.BlobTxSidecar() != nil + hash, _ = selp.Hash() + out proto.Message + err error + ) + if hasSidecar { + out = &iotextypes.ActionHash{ + Hash: hash[:], + } + } else { + out = selp.Proto() + } + if ar.messageBatcher != nil && !hasSidecar { // TODO: batch blobTx + err = ar.messageBatcher.Put(&batch.Message{ + ChainID: ar.chainID, + Target: nil, + Data: out, + }) + } else { + err = ar.broadcastHandler(context.Background(), ar.chainID, out) + } + if err != nil { + log.L().Warn("Failed to broadcast SendAction request.", zap.Error(err), zap.String("actionHash", hex.EncodeToString(hash[:]))) + } +} + +// OnRemoved does nothing +func (ar *ActionRadio) OnRemoved(act *action.SealedEnvelope) {} diff --git a/api/action_radio_test.go b/api/action_radio_test.go new file mode 100644 index 0000000000..1fec4274d3 --- /dev/null +++ b/api/action_radio_test.go @@ -0,0 +1,37 @@ +package api + +import ( + "context" + "math/big" + "sync/atomic" + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + + "github.com/iotexproject/iotex-core/v2/action" + "github.com/iotexproject/iotex-core/v2/test/identityset" +) + +func TestActionRadio(t *testing.T) { + r := require.New(t) + broadcastCount := uint64(0) + radio := NewActionRadio( + func(_ context.Context, _ uint32, _ proto.Message) error { + atomic.AddUint64(&broadcastCount, 1) + return nil + }, + 0) + r.NoError(radio.Start()) + defer func() { + r.NoError(radio.Stop()) + }() + + gas := uint64(100000) + gasPrice := big.NewInt(10) + selp, err := action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(1), 1, big.NewInt(1), nil, gas, gasPrice) + r.NoError(err) + + radio.OnAdded(selp) + r.Equal(uint64(1), atomic.LoadUint64(&broadcastCount)) +} diff --git a/api/blockdaoservice.go b/api/blockdaoservice.go index 3a1b62fa83..4a7da209c0 100644 --- a/api/blockdaoservice.go +++ b/api/blockdaoservice.go @@ -152,12 +152,7 @@ func (service *blockDAOService) FooterByHeight(_ context.Context, request *block if err != nil { return nil, err } - footerpb, err := footer.ConvertToBlockFooterPb() - if err != nil { - return nil, err - } - return &blockdaopb.FooterResponse{ - Footer: footerpb, + Footer: footer.Proto(), }, nil } diff --git a/api/coreservice.go b/api/coreservice.go index 1625d55519..45b55dd764 100644 --- a/api/coreservice.go +++ b/api/coreservice.go @@ -63,7 +63,6 @@ import ( "github.com/iotexproject/iotex-core/v2/db" "github.com/iotexproject/iotex-core/v2/gasstation" "github.com/iotexproject/iotex-core/v2/pkg/log" - batch "github.com/iotexproject/iotex-core/v2/pkg/messagebatcher" "github.com/iotexproject/iotex-core/v2/pkg/tracer" "github.com/iotexproject/iotex-core/v2/pkg/unit" "github.com/iotexproject/iotex-core/v2/pkg/version" @@ -205,7 +204,7 @@ type ( chainListener apitypes.Listener electionCommittee committee.Committee readCache *ReadCache - messageBatcher *batch.Manager + actionRadio *ActionRadio apiStats *nodestats.APILocalStats getBlockTime evm.GetBlockTime } @@ -297,9 +296,8 @@ func newCoreService( } if core.broadcastHandler != nil { - core.messageBatcher = batch.NewManager(func(msg *batch.Message) error { - return core.broadcastHandler(context.Background(), core.bc.ChainID(), msg.Data) - }) + core.actionRadio = NewActionRadio(core.broadcastHandler, core.bc.ChainID(), WithMessageBatch()) + actPool.AddSubscriber(core.actionRadio) } return &core, nil @@ -462,7 +460,7 @@ func (core *coreService) SendAction(ctx context.Context, in *iotextypes.Action) g = core.Genesis() deployer = selp.SenderAddress() ) - if selp.Encoding() == uint32(iotextypes.Encoding_ETHEREUM_UNPROTECTED) && !g.IsDeployerWhitelisted(deployer) { + if !selp.Protected() && !g.IsDeployerWhitelisted(deployer) { return "", status.Errorf(codes.InvalidArgument, "replay deployer %v not whitelisted", deployer.Hex()) } @@ -495,28 +493,6 @@ func (core *coreService) SendAction(ctx context.Context, in *iotextypes.Action) } return "", st.Err() } - // If there is no error putting into local actpool, broadcast it to the network - // broadcast action hash if it's blobTx - hasSidecar := selp.BlobTxSidecar() != nil - out := proto.Message(in) - if hasSidecar { - out = &iotextypes.ActionHash{ - Hash: hash[:], - } - } - if core.messageBatcher != nil && !hasSidecar { - // TODO: batch blobTx - err = core.messageBatcher.Put(&batch.Message{ - ChainID: core.bc.ChainID(), - Target: nil, - Data: out, - }) - } else { - err = core.broadcastHandler(ctx, core.bc.ChainID(), out) - } - if err != nil { - l.Warn("Failed to broadcast SendAction request.", zap.Error(err)) - } return hex.EncodeToString(hash[:]), nil } @@ -899,9 +875,9 @@ func (core *coreService) Start(_ context.Context) error { if err := core.chainListener.Start(); err != nil { return errors.Wrap(err, "failed to start blockchain listener") } - if core.messageBatcher != nil { - if err := core.messageBatcher.Start(); err != nil { - return errors.Wrap(err, "failed to start message batcher") + if core.actionRadio != nil { + if err := core.actionRadio.Start(); err != nil { + return errors.Wrap(err, "failed to start action radio") } } return nil @@ -909,9 +885,9 @@ func (core *coreService) Start(_ context.Context) error { // Stop stops the API server func (core *coreService) Stop(_ context.Context) error { - if core.messageBatcher != nil { - if err := core.messageBatcher.Stop(); err != nil { - return errors.Wrap(err, "failed to stop message batcher") + if core.actionRadio != nil { + if err := core.actionRadio.Stop(); err != nil { + return errors.Wrap(err, "failed to stop action radio") } } return core.chainListener.Stop() @@ -949,7 +925,7 @@ func (core *coreService) readState(ctx context.Context, p protocol.Protocol, hei if height != "" { inputHeight, err := strconv.ParseUint(height, 0, 64) if err != nil { - return nil, uint64(0), err + return nil, 0, err } rp := rolldpos.FindProtocol(core.registry) if rp != nil { @@ -961,7 +937,11 @@ func (core *coreService) readState(ctx context.Context, p protocol.Protocol, hei } if inputHeight < tipHeight { // old data, wrap to history state reader - d, h, err := p.ReadState(ctx, factory.NewHistoryStateReader(core.sf, inputHeight), methodName, arguments...) + historySR, err := core.sf.WorkingSetAtHeight(ctx, inputHeight) + if err != nil { + return nil, 0, err + } + d, h, err := p.ReadState(ctx, historySR, methodName, arguments...) if err == nil { key.Height = strconv.FormatUint(h, 10) core.readCache.Put(key.Hash(), d) diff --git a/api/grpcserver_integrity_test.go b/api/grpcserver_integrity_test.go index b415ac9783..e650f96286 100644 --- a/api/grpcserver_integrity_test.go +++ b/api/grpcserver_integrity_test.go @@ -1292,7 +1292,7 @@ func TestGrpcServer_SendActionIntegrity(t *testing.T) { cfg := newConfig() cfg.api.GRPCPort = testutil.RandomPort() cfg.genesis.MidwayBlockHeight = 10 - svr, _, _, _, _, _, bfIndexFile, err := createServerV2(cfg, true) + svr, _, _, _, _, ap, bfIndexFile, err := createServerV2(cfg, true) require.NoError(err) grpcHandler := newGRPCHandler(svr.core) defer func() { @@ -1306,7 +1306,8 @@ func TestGrpcServer_SendActionIntegrity(t *testing.T) { broadcastHandlerCount++ return nil } - coreService.messageBatcher = nil + coreService.actionRadio = NewActionRadio(coreService.broadcastHandler, 0) + ap.AddSubscriber(coreService.actionRadio) for i, test := range _sendActionTests { request := &iotexapi.SendActionRequest{Action: test.actionPb} diff --git a/api/serverV2_integrity_test.go b/api/serverV2_integrity_test.go index 7727eeb8ea..0d05ed952e 100644 --- a/api/serverV2_integrity_test.go +++ b/api/serverV2_integrity_test.go @@ -287,6 +287,7 @@ func addActsToActPool(ctx context.Context, ap actpool.ActPool) error { func setupChain(cfg testConfig) (blockchain.Blockchain, blockdao.BlockDAO, blockindex.Indexer, blockindex.BloomFilterIndexer, factory.Factory, actpool.ActPool, *protocol.Registry, string, error) { cfg.chain.ProducerPrivKey = hex.EncodeToString(identityset.PrivateKey(0).Bytes()) + cfg.chain.EnableArchiveMode = true registry := protocol.NewRegistry() factoryCfg := factory.GenerateConfig(cfg.chain, cfg.genesis) sf, err := factory.NewFactory(factoryCfg, db.NewMemKVStore(), factory.RegistryOption(registry)) diff --git a/api/web3server.go b/api/web3server.go index b7d8eefbf9..185130bcf0 100644 --- a/api/web3server.go +++ b/api/web3server.go @@ -6,11 +6,9 @@ import ( "encoding/json" "fmt" "io" - "math/big" "strconv" "time" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers/logger" @@ -32,7 +30,6 @@ import ( apitypes "github.com/iotexproject/iotex-core/v2/api/types" "github.com/iotexproject/iotex-core/v2/pkg/log" "github.com/iotexproject/iotex-core/v2/pkg/tracer" - "github.com/iotexproject/iotex-core/v2/pkg/util/addrutil" ) const ( @@ -382,10 +379,11 @@ func (svr *web3Handler) getTransactionCount(in *gjson.Result) (interface{}, erro } func (svr *web3Handler) call(in *gjson.Result) (interface{}, error) { - callerAddr, to, gasLimit, _, value, data, err := parseCallObject(in) + callMsg, err := parseCallObject(in) if err != nil { return nil, err } + callerAddr, to, gasLimit, value, data := callMsg.From, callMsg.To, callMsg.Gas, callMsg.Value, callMsg.Data if to == _metamaskBalanceContractAddr { return nil, nil } @@ -435,36 +433,21 @@ func (svr *web3Handler) call(in *gjson.Result) (interface{}, error) { } func (svr *web3Handler) estimateGas(in *gjson.Result) (interface{}, error) { - from, to, gasLimit, _, value, data, err := parseCallObject(in) + callMsg, err := parseCallObject(in) + if err != nil { + return nil, err + } + tx, err := callMsg.toUnsignedTx(svr.coreService.EVMNetworkID()) if err != nil { return nil, err } - - var ( - tx *types.Transaction - toAddr *common.Address - ) - if len(to) != 0 { - addr, err := addrutil.IoAddrToEvmAddr(to) - if err != nil { - return nil, err - } - toAddr = &addr - } - tx = types.NewTx(&types.LegacyTx{ - Nonce: 0, - GasPrice: &big.Int{}, - Gas: gasLimit, - To: toAddr, - Value: value, - Data: data, - }) elp, err := svr.ethTxToEnvelope(tx) if err != nil { return nil, err } var estimatedGas uint64 + from := callMsg.From switch act := elp.Action().(type) { case *action.Execution: estimatedGas, err = svr.coreService.EstimateExecutionGasConsumption(context.Background(), elp, from) @@ -1079,18 +1062,15 @@ func (svr *web3Handler) traceTransaction(ctx context.Context, in *gjson.Result) func (svr *web3Handler) traceCall(ctx context.Context, in *gjson.Result) (interface{}, error) { var ( - err error - contractAddr string - callData []byte - gasLimit uint64 - value *big.Int - callerAddr address.Address + err error + callMsg *callMsg ) blkNumOrHashObj, options := in.Get("params.1"), in.Get("params.2") - callerAddr, contractAddr, gasLimit, _, value, callData, err = parseCallObject(in) + callMsg, err = parseCallObject(in) if err != nil { return nil, err } + var blkNumOrHash any if blkNumOrHashObj.Exists() { blkNumOrHash = blkNumOrHashObj.Get("blockHash").String() @@ -1130,7 +1110,7 @@ func (svr *web3Handler) traceCall(ctx context.Context, in *gjson.Result) (interf }, } - retval, receipt, tracer, err := svr.coreService.TraceCall(ctx, callerAddr, blkNumOrHash, contractAddr, 0, value, gasLimit, callData, cfg) + retval, receipt, tracer, err := svr.coreService.TraceCall(ctx, callMsg.From, blkNumOrHash, callMsg.To, 0, callMsg.Value, callMsg.Gas, callMsg.Data, cfg) if err != nil { return nil, err } diff --git a/api/web3server_test.go b/api/web3server_test.go index 1ad7ea524c..4a96298600 100644 --- a/api/web3server_test.go +++ b/api/web3server_test.go @@ -392,6 +392,7 @@ func TestEstimateGas(t *testing.T) { core.EXPECT().ChainID().Return(uint32(1)).Times(2) t.Run("estimate execution", func(t *testing.T) { + core.EXPECT().EVMNetworkID().Return(uint32(0)).AnyTimes() core.EXPECT().Account(gomock.Any()).Return(&iotextypes.AccountMeta{IsContract: true}, nil, nil) core.EXPECT().EstimateExecutionGasConsumption(gomock.Any(), gomock.Any(), gomock.Any()).Return(uint64(11000), nil) diff --git a/api/web3server_utils.go b/api/web3server_utils.go index dbbc1c9de6..54f94dcd41 100644 --- a/api/web3server_utils.go +++ b/api/web3server_utils.go @@ -263,29 +263,45 @@ func parseLogRequest(in gjson.Result) (*filterObject, error) { return &logReq, nil } -func parseCallObject(in *gjson.Result) (address.Address, string, uint64, *big.Int, *big.Int, []byte, error) { +type callMsg struct { + From address.Address // the sender of the 'transaction' + To string // the destination contract (empty for contract creation) + Gas uint64 // if 0, the call executes with near-infinite gas + GasPrice *big.Int // wei <-> gas exchange ratio + GasFeeCap *big.Int // EIP-1559 fee cap per gas. + GasTipCap *big.Int // EIP-1559 tip per gas. + Value *big.Int // amount of wei sent along with the call + Data []byte // input data, usually an ABI-encoded contract method invocation + + AccessList types.AccessList // EIP-2930 access list. +} + +func parseCallObject(in *gjson.Result) (*callMsg, error) { var ( - from address.Address - to string - gasLimit uint64 - gasPrice *big.Int = big.NewInt(0) - value *big.Int = big.NewInt(0) - data []byte - err error + from address.Address + to string + gasLimit uint64 + gasPrice *big.Int = big.NewInt(0) + gasTipCap *big.Int + gasFeeCap *big.Int + value *big.Int = big.NewInt(0) + data []byte + acl types.AccessList + err error ) fromStr := in.Get("params.0.from").String() if fromStr == "" { fromStr = "0x0000000000000000000000000000000000000000" } if from, err = ethAddrToIoAddr(fromStr); err != nil { - return nil, "", 0, nil, nil, nil, err + return nil, err } toStr := in.Get("params.0.to").String() if toStr != "" { ioAddr, err := ethAddrToIoAddr(toStr) if err != nil { - return nil, "", 0, nil, nil, nil, err + return nil, err } to = ioAddr.String() } @@ -293,7 +309,7 @@ func parseCallObject(in *gjson.Result) (address.Address, string, uint64, *big.In gasStr := in.Get("params.0.gas").String() if gasStr != "" { if gasLimit, err = hexStringToNumber(gasStr); err != nil { - return nil, "", 0, nil, nil, nil, err + return nil, err } } @@ -301,7 +317,21 @@ func parseCallObject(in *gjson.Result) (address.Address, string, uint64, *big.In if gasPriceStr != "" { var ok bool if gasPrice, ok = new(big.Int).SetString(util.Remove0xPrefix(gasPriceStr), 16); !ok { - return nil, "", 0, nil, nil, nil, errors.Wrapf(errUnkownType, "gasPrice: %s", gasPriceStr) + return nil, errors.Wrapf(errUnkownType, "gasPrice: %s", gasPriceStr) + } + } + + if gasTipCapStr := in.Get("params.0.maxPriorityFeePerGas").String(); gasTipCapStr != "" { + var ok bool + if gasTipCap, ok = new(big.Int).SetString(util.Remove0xPrefix(gasTipCapStr), 16); !ok { + return nil, errors.Wrapf(errUnkownType, "gasTipCap: %s", gasTipCapStr) + } + } + + if gasFeeCapStr := in.Get("params.0.maxFeePerGas").String(); gasFeeCapStr != "" { + var ok bool + if gasFeeCap, ok = new(big.Int).SetString(util.Remove0xPrefix(gasFeeCapStr), 16); !ok { + return nil, errors.Wrapf(errUnkownType, "gasFeeCap: %s", gasFeeCapStr) } } @@ -309,7 +339,7 @@ func parseCallObject(in *gjson.Result) (address.Address, string, uint64, *big.In if valStr != "" { var ok bool if value, ok = new(big.Int).SetString(util.Remove0xPrefix(valStr), 16); !ok { - return nil, "", 0, nil, nil, nil, errors.Wrapf(errUnkownType, "value: %s", valStr) + return nil, errors.Wrapf(errUnkownType, "value: %s", valStr) } } @@ -319,7 +349,71 @@ func parseCallObject(in *gjson.Result) (address.Address, string, uint64, *big.In } else { data = common.FromHex(in.Get("params.0.data").String()) } - return from, to, gasLimit, gasPrice, value, data, nil + + if accessList := in.Get("params.0.accessList"); accessList.Exists() { + acl = types.AccessList{} + log.L().Info("raw acl", zap.String("accessList", accessList.Raw)) + if err := json.Unmarshal([]byte(accessList.Raw), &acl); err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal access list %s", accessList.Raw) + } + } + return &callMsg{ + From: from, + To: to, + Gas: gasLimit, + GasPrice: gasPrice, + GasFeeCap: gasFeeCap, + GasTipCap: gasTipCap, + Value: value, + Data: data, + AccessList: acl, + }, nil +} + +func (call *callMsg) toUnsignedTx(chainID uint32) (*types.Transaction, error) { + var ( + tx *types.Transaction + toAddr *common.Address + ) + if len(call.To) != 0 { + addr, err := addrutil.IoAddrToEvmAddr(call.To) + if err != nil { + return nil, err + } + toAddr = &addr + } + switch { + case call.GasFeeCap != nil || call.GasTipCap != nil: + tx = types.NewTx(&types.DynamicFeeTx{ + ChainID: big.NewInt(int64(chainID)), + GasTipCap: big.NewInt(0), + GasFeeCap: big.NewInt(0), + Gas: call.Gas, + To: toAddr, + Value: call.Value, + Data: call.Data, + AccessList: call.AccessList, + }) + case call.AccessList != nil: + tx = types.NewTx(&types.AccessListTx{ + ChainID: big.NewInt(int64(chainID)), + GasPrice: big.NewInt(0), + Gas: call.Gas, + To: toAddr, + Value: call.Value, + Data: call.Data, + AccessList: call.AccessList, + }) + default: + tx = types.NewTx(&types.LegacyTx{ + GasPrice: big.NewInt(0), + Gas: call.Gas, + To: toAddr, + Value: call.Value, + Data: call.Data, + }) + } + return tx, nil } func (svr *web3Handler) getLogQueryRange(fromStr, toStr string, logHeight uint64) (from uint64, to uint64, hasNewLogs bool, err error) { diff --git a/api/web3server_utils_test.go b/api/web3server_utils_test.go index 366f2d0958..9870e2412e 100644 --- a/api/web3server_utils_test.go +++ b/api/web3server_utils_test.go @@ -4,11 +4,14 @@ import ( "math/big" "testing" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/golang/mock/gomock" "github.com/iotexproject/iotex-address/address" - "github.com/iotexproject/iotex-core/v2/test/mock/mock_apicoreservice" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + + "github.com/iotexproject/iotex-core/v2/test/mock/mock_apicoreservice" ) func TestParseCallObject(t *testing.T) { @@ -18,13 +21,16 @@ func TestParseCallObject(t *testing.T) { name string input string - from string - to string - gasLimit uint64 - gasPrice *big.Int - value *big.Int - data []byte - err error + from string + to string + gasLimit uint64 + gasPrice *big.Int + gasTipCap *big.Int + gasFeeCap *big.Int + value *big.Int + data []byte + acl types.AccessList + err error }{ { name: "legacy", @@ -62,18 +68,54 @@ func TestParseCallObject(t *testing.T) { value: new(big.Int).SetInt64(1), data: []byte{0x6d, 0x4c, 0xe6, 0x3c}, }, + { + name: "dynamicfee", + input: `{"params":[{ + "from": "0x1a2f3b98e2f5a0f9f9f3f3f3f3f3f3f3f3f3f3f3", + "to": "0x7c13866F9253DEf79e20034eDD011e1d69E67fe5", + "gas": "0x4e20", + "maxFeePerGas": "0xe8d4a51000", + "maxPriorityFeePerGas": "0xd4a51000", + "value": "0x1", + "input": "0x6d4ce63c", + "accessList": [ + { + "address": "0x1a2f3b98e2f5a0f9f9f3f3f3f3f3f3f3f3f3f3f3", + "storageKeys": ["0x0000000000000000000000001a2f3b98e2f5a0f9f9f3f3f3f3f3f3f3f3f3f3f3"] + } + ] + }, + 1]}`, + from: "io1rghnhx8z7ks0n70n70el8uln70el8ulnp8hq9l", + to: "io10sfcvmuj2000083qqd8d6qg7r457vll9gly090", + gasLimit: 20000, + gasPrice: new(big.Int).SetInt64(0), + gasTipCap: new(big.Int).SetInt64(0xd4a51000), + gasFeeCap: new(big.Int).SetInt64(0xe8d4a51000), + value: new(big.Int).SetInt64(1), + data: []byte{0x6d, 0x4c, 0xe6, 0x3c}, + acl: types.AccessList{ + { + Address: common.HexToAddress("0x1a2f3b98e2f5a0f9f9f3f3f3f3f3f3f3f3f3f3f3"), + StorageKeys: []common.Hash{common.HexToHash("0x0000000000000000000000001a2f3b98e2f5a0f9f9f3f3f3f3f3f3f3f3f3f3f3")}, + }, + }, + }, } for _, test := range testData { t.Run(test.name, func(t *testing.T) { in := gjson.Parse(test.input) - from, to, gasLimit, gasPrice, value, data, err := parseCallObject(&in) - require.Equal(test.from, from.String()) - require.Equal(test.to, to) - require.Equal(test.gasLimit, gasLimit) - require.Equal(test.gasPrice, gasPrice) - require.Equal(test.value, value) - require.Equal(test.data, data) + callMsg, err := parseCallObject(&in) + require.Equal(test.from, callMsg.From.String()) + require.Equal(test.to, callMsg.To) + require.Equal(test.gasLimit, callMsg.Gas) + require.Equal(test.gasPrice, callMsg.GasPrice) + require.Equal(test.gasTipCap, callMsg.GasTipCap) + require.Equal(test.gasFeeCap, callMsg.GasFeeCap) + require.Equal(test.value, callMsg.Value) + require.Equal(test.data, callMsg.Data) + require.Equal(test.acl, callMsg.AccessList) require.Equal(test.err, err) }) } @@ -89,7 +131,7 @@ func TestParseCallObject(t *testing.T) { }, 1]}` in := gjson.Parse(input) - _, _, _, _, _, _, err := parseCallObject(&in) + _, err := parseCallObject(&in) require.EqualError(err, "gasPrice: unknown: wrong type of params") }) @@ -104,7 +146,7 @@ func TestParseCallObject(t *testing.T) { }, 1]}` in := gjson.Parse(input) - _, _, _, _, _, _, err := parseCallObject(&in) + _, err := parseCallObject(&in) require.EqualError(err, "value: unknown: wrong type of params") }) diff --git a/blockchain/block/block.go b/blockchain/block/block.go index 36d281d4de..edf1ab91fb 100644 --- a/blockchain/block/block.go +++ b/blockchain/block/block.go @@ -32,14 +32,10 @@ type Block struct { // ConvertToBlockPb converts Block to Block func (b *Block) ConvertToBlockPb() *iotextypes.Block { - footer, err := b.ConvertToBlockFooterPb() - if err != nil { - log.L().Panic("failed to convert block footer to protobuf message") - } return &iotextypes.Block{ Header: b.Header.Proto(), Body: b.Body.Proto(), - Footer: footer, + Footer: b.Footer.Proto(), } } diff --git a/blockchain/block/footer.go b/blockchain/block/footer.go index a79028760a..9922ba5f2a 100644 --- a/blockchain/block/footer.go +++ b/blockchain/block/footer.go @@ -21,20 +21,16 @@ type Footer struct { commitTime time.Time } -// ConvertToBlockFooterPb converts BlockFooter -func (f *Footer) ConvertToBlockFooterPb() (*iotextypes.BlockFooter, error) { +// Proto converts BlockFooter +func (f *Footer) Proto() *iotextypes.BlockFooter { pb := iotextypes.BlockFooter{} commitTime := timestamppb.New(f.commitTime) pb.Timestamp = commitTime pb.Endorsements = []*iotextypes.Endorsement{} for _, en := range f.endorsements { - ePb, err := en.Proto() - if err != nil { - return nil, err - } - pb.Endorsements = append(pb.Endorsements, ePb) + pb.Endorsements = append(pb.Endorsements, en.Proto()) } - return &pb, nil + return &pb } // ConvertFromBlockFooterPb converts BlockFooter to BlockFooter @@ -75,11 +71,7 @@ func (f *Footer) Endorsements() []*endorsement.Endorsement { // Serialize returns the serialized byte stream of the block footer func (f *Footer) Serialize() ([]byte, error) { - pb, err := f.ConvertToBlockFooterPb() - if err != nil { - return nil, err - } - return proto.Marshal(pb) + return proto.Marshal(f.Proto()) } // Deserialize loads from the serialized byte stream diff --git a/blockchain/block/footer_test.go b/blockchain/block/footer_test.go index 8aad685f06..9a8c946dd5 100644 --- a/blockchain/block/footer_test.go +++ b/blockchain/block/footer_test.go @@ -20,14 +20,12 @@ import ( func TestConvertToBlockFooterPb(t *testing.T) { require := require.New(t) footer := &Footer{nil, time.Now()} - blockFooter, err := footer.ConvertToBlockFooterPb() - require.NoError(err) + blockFooter := footer.Proto() require.NotNil(blockFooter) require.Equal(0, len(blockFooter.Endorsements)) footer = makeFooter() - blockFooter, err = footer.ConvertToBlockFooterPb() - require.NoError(err) + blockFooter = footer.Proto() require.NotNil(blockFooter) require.Equal(1, len(blockFooter.Endorsements)) } diff --git a/blockchain/blockdao/blockindexer.go b/blockchain/blockdao/blockindexer.go index 526c092c6b..7b3d40c3a1 100644 --- a/blockchain/blockdao/blockindexer.go +++ b/blockchain/blockdao/blockindexer.go @@ -25,7 +25,6 @@ type ( Stop(ctx context.Context) error Height() (uint64, error) PutBlock(context.Context, *block.Block) error - DeleteTipBlock(context.Context, *block.Block) error } // BlockIndexerWithStart defines an interface to accept block to build index from a start height diff --git a/blockchain/genesis/genesis.go b/blockchain/genesis/genesis.go index 9eece49354..8b3bddd224 100644 --- a/blockchain/genesis/genesis.go +++ b/blockchain/genesis/genesis.go @@ -78,7 +78,7 @@ func defaultConfig() Genesis { SumatraBlockHeight: 28516681, TsunamiBlockHeight: 29275561, UpernavikBlockHeight: 31174201, - VanuatuBlockHeight: 41174201, + VanuatuBlockHeight: 33730921, ToBeEnabledBlockHeight: math.MaxUint64, }, Account: Account{ diff --git a/blockchain/genesis/heightupgrade_test.go b/blockchain/genesis/heightupgrade_test.go index 2724c35857..33783b669e 100644 --- a/blockchain/genesis/heightupgrade_test.go +++ b/blockchain/genesis/heightupgrade_test.go @@ -65,8 +65,8 @@ func TestNewHeightChange(t *testing.T) { require.True(cfg.IsTsunami(uint64(29275561))) require.False(cfg.IsUpernavik(uint64(31174200))) require.True(cfg.IsUpernavik(uint64(31174201))) - require.False(cfg.IsVanuatu(uint64(41174200))) - require.True(cfg.IsVanuatu(uint64(41174201))) + require.False(cfg.IsVanuatu(uint64(33730920))) + require.True(cfg.IsVanuatu(uint64(33730921))) require.Equal(cfg.PacificBlockHeight, uint64(432001)) require.Equal(cfg.AleutianBlockHeight, uint64(864001)) @@ -92,5 +92,5 @@ func TestNewHeightChange(t *testing.T) { require.Equal(cfg.SumatraBlockHeight, uint64(28516681)) require.Equal(cfg.TsunamiBlockHeight, uint64(29275561)) require.Equal(cfg.UpernavikBlockHeight, uint64(31174201)) - require.Equal(cfg.VanuatuBlockHeight, uint64(41174201)) + require.Equal(cfg.VanuatuBlockHeight, uint64(33730921)) } diff --git a/blockchain/integrity/integrity_test.go b/blockchain/integrity/integrity_test.go index 6c5c0205c5..ad47ed0360 100644 --- a/blockchain/integrity/integrity_test.go +++ b/blockchain/integrity/integrity_test.go @@ -2355,14 +2355,14 @@ func testHistoryForAccount(t *testing.T, statetx bool) { // check history account's balance if statetx { - _, err = accountutil.AccountState(ctx, factory.NewHistoryStateReader(sf, bc.TipHeight()-1), a) - require.Equal(factory.ErrNotSupported, errors.Cause(err)) - _, err = accountutil.AccountState(ctx, factory.NewHistoryStateReader(sf, bc.TipHeight()-1), b) - require.Equal(factory.ErrNotSupported, errors.Cause(err)) + _, err = sf.WorkingSetAtHeight(ctx, 0) + require.NoError(err) } else { - AccountA, err = accountutil.AccountState(ctx, factory.NewHistoryStateReader(sf, bc.TipHeight()-1), a) + sr, err := sf.WorkingSetAtHeight(ctx, bc.TipHeight()-1) + require.NoError(err) + AccountA, err = accountutil.AccountState(ctx, sr, a) require.NoError(err) - AccountB, err = accountutil.AccountState(ctx, factory.NewHistoryStateReader(sf, bc.TipHeight()-1), b) + AccountB, err = accountutil.AccountState(ctx, sr, b) require.NoError(err) require.Equal(big.NewInt(100), AccountA.Balance) require.Equal(big.NewInt(100), AccountB.Balance) @@ -2403,10 +2403,11 @@ func testHistoryForContract(t *testing.T, statetx bool) { // check the the original balance again if statetx { - _, err = accountutil.AccountState(ctx, factory.NewHistoryStateReader(sf, bc.TipHeight()-1), contractAddr) - require.True(errors.Cause(err) == factory.ErrNotSupported) + _, err = sf.WorkingSetAtHeight(ctx, bc.TipHeight()-1) + require.NoError(err) } else { - sr := factory.NewHistoryStateReader(sf, bc.TipHeight()-1) + sr, err := sf.WorkingSetAtHeight(ctx, bc.TipHeight()-1) + require.NoError(err) account, err = accountutil.AccountState(ctx, sr, contractAddr) require.NoError(err) balance = BalanceOfContract(contract, genesisAccount, sr, t, account.Root) diff --git a/blockindex/contractstaking/indexer.go b/blockindex/contractstaking/indexer.go index caa9df0932..f3ae9ea560 100644 --- a/blockindex/contractstaking/indexer.go +++ b/blockindex/contractstaking/indexer.go @@ -195,11 +195,6 @@ func (s *Indexer) PutBlock(ctx context.Context, blk *block.Block) error { return s.commit(handler, blk.Height()) } -// DeleteTipBlock deletes the tip block from indexer -func (s *Indexer) DeleteTipBlock(context.Context, *block.Block) error { - return errors.New("not implemented") -} - func (s *Indexer) commit(handler *contractStakingEventHandler, height uint64) error { batch, delta := handler.Result() // update cache diff --git a/blockindex/indexer.go b/blockindex/indexer.go index c9e96b9949..c7cd07d845 100644 --- a/blockindex/indexer.go +++ b/blockindex/indexer.go @@ -49,7 +49,6 @@ type ( Stop(context.Context) error PutBlock(context.Context, *block.Block) error PutBlocks(context.Context, []*block.Block) error - DeleteTipBlock(context.Context, *block.Block) error Height() (uint64, error) GetBlockHash(height uint64) (hash.Hash256, error) GetBlockHeight(hash hash.Hash256) (uint64, error) @@ -143,49 +142,6 @@ func (x *blockIndexer) PutBlock(ctx context.Context, blk *block.Block) error { return x.commit() } -// DeleteTipBlock deletes a block's index -func (x *blockIndexer) DeleteTipBlock(ctx context.Context, blk *block.Block) error { - x.mutex.Lock() - defer x.mutex.Unlock() - - // the block to be deleted must be exactly current top, otherwise counting index would not work correctly - height := blk.Height() - if height != x.tbk.Size()-1 { - return errors.Wrapf(db.ErrInvalid, "wrong block height %d, expecting %d", height, x.tbk.Size()-1) - } - // delete hash --> height - hash := blk.HashBlock() - x.batch.Delete(_blockHashToHeightNS, hash[_hashOffset:], fmt.Sprintf("failed to delete block at height %d", height)) - // delete from total block index - if err := x.tbk.Revert(1); err != nil { - return err - } - - // delete action index - fCtx := protocol.MustGetFeatureCtx(protocol.WithFeatureCtx(protocol.WithBlockCtx(ctx, protocol.BlockCtx{ - BlockHeight: blk.Height(), - }))) - for _, selp := range blk.Actions { - actHash, err := selp.Hash() - if err != nil { - return err - } - x.batch.Delete(_actionToBlockHashNS, actHash[_hashOffset:], fmt.Sprintf("failed to delete action hash %x", actHash)) - if err := x.indexAction(actHash, selp, false, fCtx.TolerateLegacyAddress); err != nil { - return err - } - } - // delete from total action index - if err := x.tac.Revert(uint64(len(blk.Actions))); err != nil { - return err - } - if err := x.kvStore.WriteBatch(x.batch); err != nil { - return err - } - x.batch.Clear() - return nil -} - // Height return the blockchain height func (x *blockIndexer) Height() (uint64, error) { x.mutex.RLock() @@ -340,7 +296,7 @@ func (x *blockIndexer) putBlock(ctx context.Context, blk *block.Block) error { if err := x.tac.Add(actHash[:], true); err != nil { return err } - if err := x.indexAction(actHash, selp, true, fCtx.TolerateLegacyAddress); err != nil { + if err := x.indexAction(actHash, selp, fCtx.TolerateLegacyAddress); err != nil { return err } } @@ -377,10 +333,7 @@ func (x *blockIndexer) commit() error { // getIndexerForAddr returns the counting indexer for an address // if batch is true, the indexer will be placed into a dirty map, to be committed later -func (x *blockIndexer) getIndexerForAddr(addr []byte, batch bool) (db.CountingIndex, error) { - if !batch { - return db.NewCountingIndexNX(x.kvStore, addr) - } +func (x *blockIndexer) getIndexerForAddr(addr []byte) (db.CountingIndex, error) { address := hash.BytesToHash160(addr) indexer, ok := x.dirtyAddr[address] if !ok { @@ -399,19 +352,14 @@ func (x *blockIndexer) getIndexerForAddr(addr []byte, batch bool) (db.CountingIn } // indexAction builds index for an action -func (x *blockIndexer) indexAction(actHash hash.Hash256, elp *action.SealedEnvelope, insert, tolerateLegacyAddress bool) error { +func (x *blockIndexer) indexAction(actHash hash.Hash256, elp *action.SealedEnvelope, tolerateLegacyAddress bool) error { // add to sender's index callerAddrBytes := elp.SrcPubkey().Hash() - sender, err := x.getIndexerForAddr(callerAddrBytes, insert) + sender, err := x.getIndexerForAddr(callerAddrBytes) if err != nil { return err } - if insert { - err = sender.Add(actHash[:], insert) - } else { - err = sender.Revert(1) - } - if err != nil { + if err = sender.Add(actHash[:], true); err != nil { return err } @@ -437,14 +385,9 @@ func (x *blockIndexer) indexAction(actHash hash.Hash256, elp *action.SealedEnvel } // add to recipient's index - recipient, err := x.getIndexerForAddr(dstAddrBytes, insert) + recipient, err := x.getIndexerForAddr(dstAddrBytes) if err != nil { return err } - if insert { - err = recipient.Add(actHash[:], insert) - } else { - err = recipient.Revert(1) - } - return err + return recipient.Add(actHash[:], true) } diff --git a/blockindex/indexer_test.go b/blockindex/indexer_test.go index a8e1769e9e..cbbb2ad942 100644 --- a/blockindex/indexer_test.go +++ b/blockindex/indexer_test.go @@ -258,54 +258,6 @@ func TestIndexer(t *testing.T) { require.NoError(err) require.EqualValues(len(indexTests[0].actions[i].hashes), actionCount) } - - // delete tip block one by one, verify address/action after each deletion - for i := range indexTests { - if i == 0 { - // tests[0] is the whole address/action data at block height 3 - continue - } - - require.NoError(indexer.DeleteTipBlock(ctx, blks[3-i])) - tipHeight, err := indexer.Height() - require.NoError(err) - require.EqualValues(uint64(3-i), tipHeight) - h, err := indexer.GetBlockHash(tipHeight) - require.NoError(err) - if i <= 2 { - require.Equal(blks[2-i].HashBlock(), h) - } else { - require.Equal(hash.ZeroHash256, h) - } - - total, err := indexer.GetTotalActions() - require.NoError(err) - require.EqualValues(indexTests[i].total, total) - if total > 0 { - _, err = indexer.GetActionHashFromIndex(1, total) - require.Equal(db.ErrInvalid, errors.Cause(err)) - actions, err := indexer.GetActionHashFromIndex(0, total) - require.NoError(err) - require.Equal(actions, indexTests[i].hashTotal) - } - for j := range indexTests[i].actions { - actionCount, err := indexer.GetActionCountByAddress(indexTests[i].actions[j].addr) - require.NoError(err) - require.EqualValues(len(indexTests[i].actions[j].hashes), actionCount) - if actionCount > 0 { - actions, err := indexer.GetActionsByAddress(indexTests[i].actions[j].addr, 0, actionCount) - require.NoError(err) - require.Equal(actions, indexTests[i].actions[j].hashes) - } - } - } - - tipHeight, err := indexer.Height() - require.NoError(err) - require.EqualValues(0, tipHeight) - total, err := indexer.GetTotalActions() - require.NoError(err) - require.EqualValues(0, total) } t.Run("In-memory KV indexer", func(t *testing.T) { diff --git a/blockindex/sync_indexers.go b/blockindex/sync_indexers.go index 9006c73258..f35cc6247f 100644 --- a/blockindex/sync_indexers.go +++ b/blockindex/sync_indexers.go @@ -69,16 +69,6 @@ func (ig *SyncIndexers) PutBlock(ctx context.Context, blk *block.Block) error { return nil } -// DeleteTipBlock deletes the tip block from the indexers in the group -func (ig *SyncIndexers) DeleteTipBlock(ctx context.Context, blk *block.Block) error { - for _, indexer := range ig.indexers { - if err := indexer.DeleteTipBlock(ctx, blk); err != nil { - return err - } - } - return nil -} - // StartHeight returns the minimum start height of the indexers in the group func (ig *SyncIndexers) StartHeight() uint64 { return ig.minStartHeight diff --git a/blocksync/blocksync.go b/blocksync/blocksync.go index d06d30dd98..661e11c827 100644 --- a/blocksync/blocksync.go +++ b/blocksync/blocksync.go @@ -290,7 +290,6 @@ func (bs *blockSyncer) ProcessBlock(ctx context.Context, peer string, blk *block } syncedHeight++ } - bs.buf.Cleanup(syncedHeight) log.L().Debug("flush blocks", zap.Uint64("start", tip), zap.Uint64("end", syncedHeight)) if syncedHeight > bs.lastTip { bs.lastTip = syncedHeight diff --git a/blocksync/buffer.go b/blocksync/buffer.go index 459047f522..ea1538f015 100644 --- a/blocksync/buffer.go +++ b/blocksync/buffer.go @@ -7,10 +7,6 @@ package blocksync import ( "sync" - - "go.uber.org/zap" - - "github.com/iotexproject/iotex-core/v2/pkg/log" ) // blockBuffer is used to keep in-coming block in order. @@ -47,23 +43,6 @@ func (b *blockBuffer) Pop(height uint64) []*peerBlock { return blks } -func (b *blockBuffer) Cleanup(height uint64) { - b.mu.Lock() - defer b.mu.Unlock() - - size := len(b.blockQueues) - if size > int(b.bufferSize)*2 { - log.L().Warn("blockBuffer is leaking memory.", zap.Int("bufferSize", size)) - newQueues := map[uint64]*uniQueue{} - for h := range b.blockQueues { - if h > height { - newQueues[h] = b.blockQueues[h] - } - } - b.blockQueues = newQueues - } -} - // AddBlock tries to put given block into buffer and flush buffer into blockchain. func (b *blockBuffer) AddBlock(tipHeight uint64, blk *peerBlock) (bool, uint64) { b.mu.Lock() diff --git a/consensus/scheme/rolldpos/blockproposal.go b/consensus/scheme/rolldpos/blockproposal.go index 76cf87308d..e98fd8c0d6 100644 --- a/consensus/scheme/rolldpos/blockproposal.go +++ b/consensus/scheme/rolldpos/blockproposal.go @@ -34,11 +34,7 @@ func (bp *blockProposal) Proto() (*iotextypes.BlockProposal, error) { bPb := bp.block.ConvertToBlockPb() endorsements := []*iotextypes.Endorsement{} for _, en := range bp.proofOfLock { - ePb, err := en.Proto() - if err != nil { - return nil, err - } - endorsements = append(endorsements, ePb) + endorsements = append(endorsements, en.Proto()) } return &iotextypes.BlockProposal{ Block: bPb, diff --git a/consensus/scheme/rolldpos/endorsedconsensusmessage.go b/consensus/scheme/rolldpos/endorsedconsensusmessage.go index fd018c4b53..42c3f4b112 100644 --- a/consensus/scheme/rolldpos/endorsedconsensusmessage.go +++ b/consensus/scheme/rolldpos/endorsedconsensusmessage.go @@ -50,13 +50,9 @@ func (ecm *EndorsedConsensusMessage) Height() uint64 { // Proto converts an endorsement to endorse proto func (ecm *EndorsedConsensusMessage) Proto() (*iotextypes.ConsensusMessage, error) { - ebp, err := ecm.endorsement.Proto() - if err != nil { - return nil, err - } cmsg := &iotextypes.ConsensusMessage{ Height: ecm.height, - Endorsement: ebp, + Endorsement: ecm.endorsement.Proto(), } switch message := ecm.message.(type) { case *ConsensusVote: diff --git a/consensus/scheme/rolldpos/endorsementmanager.go b/consensus/scheme/rolldpos/endorsementmanager.go index de7819bde4..f977c038d3 100644 --- a/consensus/scheme/rolldpos/endorsementmanager.go +++ b/consensus/scheme/rolldpos/endorsementmanager.go @@ -60,11 +60,7 @@ func (ee *endorserEndorsementCollection) toProto(endorser string) (*endorsementp eeProto.Endorser = endorser for topic, endorse := range ee.endorsements { eeProto.Topics = append(eeProto.Topics, uint32(topic)) - ioEndorsement, err := endorse.Proto() - if err != nil { - return nil, err - } - eeProto.Endorsements = append(eeProto.Endorsements, ioEndorsement) + eeProto.Endorsements = append(eeProto.Endorsements, endorse.Proto()) } return eeProto, nil } diff --git a/consensus/scheme/rolldpos/endorsementmanager_test.go b/consensus/scheme/rolldpos/endorsementmanager_test.go index 423f450ca1..2c49090ea6 100644 --- a/consensus/scheme/rolldpos/endorsementmanager_test.go +++ b/consensus/scheme/rolldpos/endorsementmanager_test.go @@ -195,10 +195,8 @@ func TestEndorsementManagerProto(t *testing.T) { require.NoError(em.SetMintedBlock(&b)) //test converting endorsement pb - endProto, err := end.Proto() - require.NoError(err) end2 := &endorsement.Endorsement{} - require.NoError(end2.LoadProto(endProto)) + require.NoError(end2.LoadProto(end.Proto())) require.Equal(end, end2) //test converting emanager pb diff --git a/consensus/scheme/rolldpos/rolldpos_test.go b/consensus/scheme/rolldpos/rolldpos_test.go index 1301a230f6..cbcb3ccd42 100644 --- a/consensus/scheme/rolldpos/rolldpos_test.go +++ b/consensus/scheme/rolldpos/rolldpos_test.go @@ -179,9 +179,7 @@ func makeBlock(t *testing.T, accountIndex, numOfEndosements int, makeInvalidEndo } en, err := endorsement.Endorse(identityset.PrivateKey(i), consensusVote, timeTime) require.NoError(t, err) - enProto, err := en.Proto() - require.NoError(t, err) - typesFooter.Endorsements = append(typesFooter.Endorsements, enProto) + typesFooter.Endorsements = append(typesFooter.Endorsements, en.Proto()) } ts := timestamppb.New(time.Unix(int64(unixTime), 0)) typesFooter.Timestamp = ts diff --git a/endorsement/endorsement.go b/endorsement/endorsement.go index fcc5f7bdc2..6884a4ccd0 100644 --- a/endorsement/endorsement.go +++ b/endorsement/endorsement.go @@ -113,13 +113,13 @@ func (en *Endorsement) Signature() []byte { } // Proto converts an endorsement to protobuf message -func (en *Endorsement) Proto() (*iotextypes.Endorsement, error) { +func (en *Endorsement) Proto() *iotextypes.Endorsement { ts := timestamppb.New(en.ts) return &iotextypes.Endorsement{ Timestamp: ts, Endorser: en.endorser.Bytes(), Signature: en.Signature(), - }, nil + } } // LoadProto converts a protobuf message to endorsement diff --git a/go.mod b/go.mod index fbec453dfb..7bc3c3af2c 100644 --- a/go.mod +++ b/go.mod @@ -241,7 +241,7 @@ require ( lukechampine.com/blake3 v1.2.1 // indirect ) -//TODO: add tag for go-ethereum +//Note: add tag for go-ethereum before cutting hard-fork release replace github.com/ethereum/go-ethereum => github.com/iotexproject/go-ethereum v0.5.0 replace golang.org/x/xerrors => golang.org/x/xerrors v0.0.0-20190212162355-a5947ffaace3 diff --git a/misc/scripts/mockgen.sh b/misc/scripts/mockgen.sh index f372dd8ce9..c2f5deb9d7 100755 --- a/misc/scripts/mockgen.sh +++ b/misc/scripts/mockgen.sh @@ -52,10 +52,7 @@ mockgen -destination=./test/mock/mock_lifecycle/mock_lifecycle.go \ mkdir -p ./test/mock/mock_actpool mockgen -destination=./test/mock/mock_actpool/mock_actpool.go \ - -source=./actpool/actpool.go \ - -self_package=github.com/iotexproject/iotex-core/v2/actpool \ - -package=mock_actpool \ - ActPool + github.com/iotexproject/iotex-core/v2/actpool ActPool mkdir -p ./test/mock/mock_actioniterator mockgen -destination=./test/mock/mock_actioniterator/mock_actioniterator.go \ diff --git a/state/factory/factory.go b/state/factory/factory.go index edd913e16d..228f5ca268 100644 --- a/state/factory/factory.go +++ b/state/factory/factory.go @@ -86,9 +86,6 @@ type ( // NewBlockBuilder creates block builder NewBlockBuilder(context.Context, actpool.ActPool, func(action.Envelope) (*action.SealedEnvelope, error)) (*block.Builder, error) PutBlock(context.Context, *block.Block) error - DeleteTipBlock(context.Context, *block.Block) error - StateAtHeight(uint64, interface{}, ...protocol.StateOption) error - StatesAtHeight(uint64, ...protocol.StateOption) (state.Iterator, error) WorkingSet(context.Context) (protocol.StateManager, error) WorkingSetAtHeight(context.Context, uint64) (protocol.StateManager, error) } @@ -272,6 +269,31 @@ func (sf *factory) newWorkingSet(ctx context.Context, height uint64) (*workingSe if err != nil { return nil, err } + return sf.createSfWorkingSet(ctx, height, store) +} + +func (sf *factory) newWorkingSetAtHeight(ctx context.Context, height uint64) (*workingSet, error) { + span := tracer.SpanFromContext(ctx) + span.AddEvent("factory.newWorkingSet") + defer span.End() + + g := genesis.MustExtractGenesisContext(ctx) + flusher, err := db.NewKVStoreFlusher( + sf.dao, + batch.NewCachedBatch(), + sf.flusherOptions(!g.IsEaster(height))..., + ) + if err != nil { + return nil, err + } + store, err := newFactoryWorkingSetStoreAtHeight(sf.protocolView, flusher, height) + if err != nil { + return nil, err + } + return sf.createSfWorkingSet(ctx, height, store) +} + +func (sf *factory) createSfWorkingSet(ctx context.Context, height uint64, store workingSetStore) (*workingSet, error) { if err := store.Start(ctx); err != nil { return nil, err } @@ -286,7 +308,6 @@ func (sf *factory) newWorkingSet(ctx context.Context, height uint64) (*workingSe } } } - return newWorkingSet(height, store), nil } @@ -388,16 +409,20 @@ func (sf *factory) WorkingSet(ctx context.Context) (protocol.StateManager, error } func (sf *factory) WorkingSetAtHeight(ctx context.Context, height uint64) (protocol.StateManager, error) { + if !sf.saveHistory { + return nil, ErrNoArchiveData + } sf.mutex.Lock() defer sf.mutex.Unlock() - return sf.newWorkingSet(ctx, height+1) + if height > sf.currentChainHeight { + return nil, errors.Errorf("query height %d is higher than tip height %d", height, sf.currentChainHeight) + } + return sf.newWorkingSetAtHeight(ctx, height) } // PutBlock persists all changes in RunActions() into the DB func (sf *factory) PutBlock(ctx context.Context, blk *block.Block) error { - sf.mutex.Lock() timer := sf.timerFactory.NewTimer("Commit") - sf.mutex.Unlock() defer timer.End() producer := blk.PublicKey().Address() if producer == nil { @@ -452,56 +477,6 @@ func (sf *factory) PutBlock(ctx context.Context, blk *block.Block) error { return nil } -func (sf *factory) DeleteTipBlock(_ context.Context, _ *block.Block) error { - return errors.Wrap(ErrNotSupported, "cannot delete tip block from factory") -} - -// StateAtHeight returns a confirmed state at height -- archive mode -func (sf *factory) StateAtHeight(height uint64, s interface{}, opts ...protocol.StateOption) error { - sf.mutex.RLock() - defer sf.mutex.RUnlock() - cfg, err := processOptions(opts...) - if err != nil { - return err - } - if cfg.Keys != nil { - return errors.Wrap(ErrNotSupported, "Read state with keys option has not been implemented yet") - } - if height > sf.currentChainHeight { - return errors.Errorf("query height %d is higher than tip height %d", height, sf.currentChainHeight) - } - return sf.stateAtHeight(height, cfg.Namespace, cfg.Key, s) -} - -// StatesAtHeight returns a set states in the state factory at height -- archive mode -func (sf *factory) StatesAtHeight(height uint64, opts ...protocol.StateOption) (state.Iterator, error) { - sf.mutex.RLock() - defer sf.mutex.RUnlock() - if height > sf.currentChainHeight { - return nil, errors.Errorf("query height %d is higher than tip height %d", height, sf.currentChainHeight) - } - cfg, err := processOptions(opts...) - if err != nil { - return nil, err - } - if cfg.Keys != nil { - return nil, errors.Wrap(ErrNotSupported, "Read states with keys option has not been implemented yet") - } - tlt, err := newTwoLayerTrie(ArchiveTrieNamespace, sf.dao, fmt.Sprintf("%s-%d", ArchiveTrieRootKey, height), false) - if err != nil { - return nil, errors.Wrapf(err, "failed to generate trie for %d", height) - } - if err := tlt.Start(context.Background()); err != nil { - return nil, err - } - defer tlt.Stop(context.Background()) - keys, values, err := readStatesFromTLT(tlt, cfg.Namespace, cfg.Keys) - if err != nil { - return nil, err - } - return state.NewIterator(keys, values) -} - // State returns a confirmed state in the state factory func (sf *factory) State(s interface{}, opts ...protocol.StateOption) (uint64, error) { sf.mutex.RLock() @@ -573,26 +548,6 @@ func legacyKeyLen() int { return 20 } -func (sf *factory) stateAtHeight(height uint64, ns string, key []byte, s interface{}) error { - if !sf.saveHistory { - return ErrNoArchiveData - } - tlt, err := newTwoLayerTrie(ArchiveTrieNamespace, sf.dao, fmt.Sprintf("%s-%d", ArchiveTrieRootKey, height), false) - if err != nil { - return errors.Wrapf(err, "failed to generate trie for %d", height) - } - if err := tlt.Start(context.Background()); err != nil { - return err - } - defer tlt.Stop(context.Background()) - - value, err := readStateFromTLT(tlt, ns, key) - if err != nil { - return err - } - return state.Deserialize(s, value) -} - func (sf *factory) createGenesisStates(ctx context.Context) error { ws, err := sf.newWorkingSet(ctx, 0) if err != nil { diff --git a/state/factory/factory_test.go b/state/factory/factory_test.go index 97ebf293fa..d6f4b8d204 100644 --- a/state/factory/factory_test.go +++ b/state/factory/factory_test.go @@ -359,11 +359,11 @@ func TestState(t *testing.T) { func TestHistoryState(t *testing.T) { r := require.New(t) - var err error // using factory and enable history cfg := DefaultConfig - cfg.Chain.TrieDBPath, err = testutil.PathOfTempFile(_triePath) + file1, err := testutil.PathOfTempFile(_triePath) r.NoError(err) + cfg.Chain.TrieDBPath = file1 cfg.Chain.EnableArchiveMode = true db1, err := db.CreateKVStore(db.DefaultConfig, cfg.Chain.TrieDBPath) r.NoError(err) @@ -372,8 +372,9 @@ func TestHistoryState(t *testing.T) { testHistoryState(sf, t, false, cfg.Chain.EnableArchiveMode) // using stateDB and enable history - cfg.Chain.TrieDBPath, err = testutil.PathOfTempFile(_triePath) + file2, err := testutil.PathOfTempFile(_triePath) r.NoError(err) + cfg.Chain.TrieDBPath = file2 db2, err := db.CreateKVStoreWithCache(db.DefaultConfig, cfg.Chain.TrieDBPath, cfg.Chain.StateDBCacheSize) r.NoError(err) sf, err = NewStateDB(cfg, db2, SkipBlockValidationStateDBOption()) @@ -381,8 +382,9 @@ func TestHistoryState(t *testing.T) { testHistoryState(sf, t, true, cfg.Chain.EnableArchiveMode) // using factory and disable history - cfg.Chain.TrieDBPath, err = testutil.PathOfTempFile(_triePath) + file3, err := testutil.PathOfTempFile(_triePath) r.NoError(err) + cfg.Chain.TrieDBPath = file3 cfg.Chain.EnableArchiveMode = false db1, err = db.CreateKVStore(db.DefaultConfig, cfg.Chain.TrieDBPath) r.NoError(err) @@ -391,15 +393,19 @@ func TestHistoryState(t *testing.T) { testHistoryState(sf, t, false, cfg.Chain.EnableArchiveMode) // using stateDB and disable history - cfg.Chain.TrieDBPath, err = testutil.PathOfTempFile(_triePath) + file4, err := testutil.PathOfTempFile(_triePath) r.NoError(err) + cfg.Chain.TrieDBPath = file4 db2, err = db.CreateKVStoreWithCache(db.DefaultConfig, cfg.Chain.TrieDBPath, cfg.Chain.StateDBCacheSize) r.NoError(err) sf, err = NewStateDB(cfg, db2, SkipBlockValidationStateDBOption()) r.NoError(err) testHistoryState(sf, t, true, cfg.Chain.EnableArchiveMode) defer func() { - testutil.CleanupPath(cfg.Chain.TrieDBPath) + testutil.CleanupPath(file1) + testutil.CleanupPath(file2) + testutil.CleanupPath(file3) + testutil.CleanupPath(file4) }() } @@ -567,21 +573,24 @@ func testHistoryState(sf Factory, t *testing.T, statetx, archive bool) { // check archive data if statetx { - // statetx not support archive mode - _, err = accountutil.AccountState(ctx, NewHistoryStateReader(sf, 0), a) - require.Equal(t, ErrNotSupported, errors.Cause(err)) - _, err = accountutil.AccountState(ctx, NewHistoryStateReader(sf, 0), b) - require.Equal(t, ErrNotSupported, errors.Cause(err)) + // statetx not support archive mode yet + _, err = sf.WorkingSetAtHeight(ctx, 0) + require.NoError(t, err) } else { + _, err = sf.WorkingSetAtHeight(ctx, 10) if !archive { - _, err = accountutil.AccountState(ctx, NewHistoryStateReader(sf, 0), a) require.Equal(t, ErrNoArchiveData, errors.Cause(err)) - _, err = accountutil.AccountState(ctx, NewHistoryStateReader(sf, 0), b) + } else { + require.Contains(t, err.Error(), "query height 10 is higher than tip height 1") + } + sr, err := sf.WorkingSetAtHeight(ctx, 0) + if !archive { require.Equal(t, ErrNoArchiveData, errors.Cause(err)) } else { - accountA, err = accountutil.AccountState(ctx, NewHistoryStateReader(sf, 0), a) require.NoError(t, err) - accountB, err = accountutil.AccountState(ctx, NewHistoryStateReader(sf, 0), b) + accountA, err = accountutil.AccountState(ctx, sr, a) + require.NoError(t, err) + accountB, err = accountutil.AccountState(ctx, sr, b) require.NoError(t, err) require.Equal(t, big.NewInt(100), accountA.Balance) require.Equal(t, big.NewInt(0), accountB.Balance) diff --git a/state/factory/historyfactory.go b/state/factory/historyfactory.go deleted file mode 100644 index d2e8c6ad26..0000000000 --- a/state/factory/historyfactory.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2020 IoTeX Foundation -// This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability -// or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. -// This source code is governed by Apache License 2.0 that can be found in the LICENSE file. - -package factory - -import ( - "github.com/iotexproject/iotex-core/v2/action/protocol" - "github.com/iotexproject/iotex-core/v2/state" -) - -// historyStateReader implements state reader interface, wrap factory with archive height -type historyStateReader struct { - height uint64 - sf Factory -} - -// NewHistoryStateReader creates new history state reader by given state factory and height -func NewHistoryStateReader(sf Factory, h uint64) protocol.StateReader { - return &historyStateReader{ - sf: sf, - height: h, - } -} - -// Height returns archive height -func (hReader *historyStateReader) Height() (uint64, error) { - return hReader.height, nil -} - -// State returns history state in the archive mode state factory -func (hReader *historyStateReader) State(s interface{}, opts ...protocol.StateOption) (uint64, error) { - return hReader.height, hReader.sf.StateAtHeight(hReader.height, s, opts...) -} - -// States returns history states in the archive mode state factory -func (hReader *historyStateReader) States(opts ...protocol.StateOption) (uint64, state.Iterator, error) { - iterator, err := hReader.sf.StatesAtHeight(hReader.height, opts...) - if err != nil { - return 0, nil, err - } - return hReader.height, iterator, nil -} - -// ReadView reads the view -func (hReader *historyStateReader) ReadView(name string) (interface{}, error) { - return hReader.sf.ReadView(name) -} diff --git a/state/factory/statedb.go b/state/factory/statedb.go index 7da193a25f..cf5fc01f84 100644 --- a/state/factory/statedb.go +++ b/state/factory/statedb.go @@ -267,7 +267,8 @@ func (sdb *stateDB) WorkingSet(ctx context.Context) (protocol.StateManager, erro } func (sdb *stateDB) WorkingSetAtHeight(ctx context.Context, height uint64) (protocol.StateManager, error) { - return sdb.newWorkingSet(ctx, height+1) + // TODO: implement archive mode + return sdb.newWorkingSet(ctx, height) } // PutBlock persists all changes in RunActions() into the DB @@ -320,10 +321,6 @@ func (sdb *stateDB) PutBlock(ctx context.Context, blk *block.Block) error { return nil } -func (sdb *stateDB) DeleteTipBlock(_ context.Context, _ *block.Block) error { - return errors.Wrap(ErrNotSupported, "cannot delete tip block from state db") -} - // State returns a confirmed state in the state factory func (sdb *stateDB) State(s interface{}, opts ...protocol.StateOption) (uint64, error) { cfg, err := processOptions(opts...) diff --git a/state/factory/workingset.go b/state/factory/workingset.go index 5359bd4a67..cb28cf880a 100644 --- a/state/factory/workingset.go +++ b/state/factory/workingset.go @@ -194,7 +194,7 @@ func (ws *workingSet) runAction( } // for replay tx, check against deployer whitelist g := genesis.MustExtractGenesisContext(ctx) - if selp.Encoding() == uint32(iotextypes.Encoding_ETHEREUM_UNPROTECTED) && !g.IsDeployerWhitelisted(selp.SenderAddress()) { + if !selp.Protected() && !g.IsDeployerWhitelisted(selp.SenderAddress()) { return nil, errors.Wrap(errDeployerNotWhitelisted, selp.SenderAddress().String()) } // Handle action @@ -343,6 +343,9 @@ func (ws *workingSet) State(s interface{}, opts ...protocol.StateOption) (uint64 if err != nil { return ws.height, err } + if cfg.Keys != nil { + return 0, errors.Wrap(ErrNotSupported, "Read state with keys option has not been implemented yet") + } value, err := ws.store.Get(cfg.Namespace, cfg.Key) if err != nil { return ws.height, err diff --git a/state/factory/workingset_test.go b/state/factory/workingset_test.go index fd1e5c1841..78f752ed19 100644 --- a/state/factory/workingset_test.go +++ b/state/factory/workingset_test.go @@ -57,7 +57,9 @@ func newFactoryWorkingSet(t testing.TB) *workingSet { genesis.Default, ) r.NoError(sf.Start(ctx)) - // defer r.NoError(sf.Stop(ctx)) + defer func() { + r.NoError(sf.Stop(ctx)) + }() ws, err := sf.(workingSetCreator).newWorkingSet(ctx, 1) r.NoError(err) diff --git a/state/factory/workingsetstore.go b/state/factory/workingsetstore.go index c357674be6..cec2d453e5 100644 --- a/state/factory/workingsetstore.go +++ b/state/factory/workingsetstore.go @@ -70,6 +70,21 @@ func newFactoryWorkingSetStore(view protocol.View, flusher db.KVStoreFlusher) (w }, nil } +func newFactoryWorkingSetStoreAtHeight(view protocol.View, flusher db.KVStoreFlusher, height uint64) (workingSetStore, error) { + rootKey := fmt.Sprintf("%s-%d", ArchiveTrieRootKey, height) + tlt, err := newTwoLayerTrie(ArchiveTrieNamespace, flusher.KVStoreWithBuffer(), rootKey, false) + if err != nil { + return nil, err + } + + return &factoryWorkingSetStore{ + flusher: flusher, + view: view, + tlt: tlt, + trieRoots: make(map[int][]byte), + }, nil +} + func (store *stateDBWorkingSetStore) Start(context.Context) error { return nil } diff --git a/systemcontractindex/stakingindex/index.go b/systemcontractindex/stakingindex/index.go index b5e2b13c03..b0301f6347 100644 --- a/systemcontractindex/stakingindex/index.go +++ b/systemcontractindex/stakingindex/index.go @@ -40,7 +40,6 @@ type ( BucketsByCandidate(candidate address.Address, height uint64) ([]*VoteBucket, error) TotalBucketCount(height uint64) (uint64, error) PutBlock(ctx context.Context, blk *block.Block) error - DeleteTipBlock(ctx context.Context, blk *block.Block) error } // Indexer is the staking indexer Indexer struct { @@ -213,11 +212,6 @@ func (s *Indexer) PutBlock(ctx context.Context, blk *block.Block) error { return s.commit(handler, blk.Height()) } -// DeleteTipBlock deletes the tip block from indexer -func (s *Indexer) DeleteTipBlock(context.Context, *block.Block) error { - return errors.New("not implemented") -} - func (s *Indexer) commit(handler *eventHandler, height uint64) error { delta, dirty := handler.Finalize() // update db diff --git a/test/mock/mock_actpool/mock_actpool.go b/test/mock/mock_actpool/mock_actpool.go index 015dbf911b..02c59975fd 100644 --- a/test/mock/mock_actpool/mock_actpool.go +++ b/test/mock/mock_actpool/mock_actpool.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: ./actpool/actpool.go +// Source: github.com/iotexproject/iotex-core/v2/actpool (interfaces: ActPool) // Package mock_actpool is a generated GoMock package. package mock_actpool @@ -12,6 +12,7 @@ import ( hash "github.com/iotexproject/go-pkgs/hash" address "github.com/iotexproject/iotex-address/address" action "github.com/iotexproject/iotex-core/v2/action" + actpool "github.com/iotexproject/iotex-core/v2/actpool" block "github.com/iotexproject/iotex-core/v2/blockchain/block" ) @@ -39,17 +40,17 @@ func (m *MockActPool) EXPECT() *MockActPoolMockRecorder { } // Add mocks base method. -func (m *MockActPool) Add(ctx context.Context, act *action.SealedEnvelope) error { +func (m *MockActPool) Add(arg0 context.Context, arg1 *action.SealedEnvelope) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Add", ctx, act) + ret := m.ctrl.Call(m, "Add", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // Add indicates an expected call of Add. -func (mr *MockActPoolMockRecorder) Add(ctx, act interface{}) *gomock.Call { +func (mr *MockActPoolMockRecorder) Add(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockActPool)(nil).Add), ctx, act) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockActPool)(nil).Add), arg0, arg1) } // AddActionEnvelopeValidators mocks base method. @@ -68,6 +69,18 @@ func (mr *MockActPoolMockRecorder) AddActionEnvelopeValidators(arg0 ...interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddActionEnvelopeValidators", reflect.TypeOf((*MockActPool)(nil).AddActionEnvelopeValidators), arg0...) } +// AddSubscriber mocks base method. +func (m *MockActPool) AddSubscriber(arg0 actpool.Subscriber) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddSubscriber", arg0) +} + +// AddSubscriber indicates an expected call of AddSubscriber. +func (mr *MockActPoolMockRecorder) AddSubscriber(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSubscriber", reflect.TypeOf((*MockActPool)(nil).AddSubscriber), arg0) +} + // DeleteAction mocks base method. func (m *MockActPool) DeleteAction(arg0 address.Address) { m.ctrl.T.Helper() @@ -81,18 +94,18 @@ func (mr *MockActPoolMockRecorder) DeleteAction(arg0 interface{}) *gomock.Call { } // GetActionByHash mocks base method. -func (m *MockActPool) GetActionByHash(hash hash.Hash256) (*action.SealedEnvelope, error) { +func (m *MockActPool) GetActionByHash(arg0 hash.Hash256) (*action.SealedEnvelope, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetActionByHash", hash) + ret := m.ctrl.Call(m, "GetActionByHash", arg0) ret0, _ := ret[0].(*action.SealedEnvelope) ret1, _ := ret[1].(error) return ret0, ret1 } // GetActionByHash indicates an expected call of GetActionByHash. -func (mr *MockActPoolMockRecorder) GetActionByHash(hash interface{}) *gomock.Call { +func (mr *MockActPoolMockRecorder) GetActionByHash(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActionByHash", reflect.TypeOf((*MockActPool)(nil).GetActionByHash), hash) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActionByHash", reflect.TypeOf((*MockActPool)(nil).GetActionByHash), arg0) } // GetCapacity mocks base method. @@ -138,18 +151,18 @@ func (mr *MockActPoolMockRecorder) GetGasSize() *gomock.Call { } // GetPendingNonce mocks base method. -func (m *MockActPool) GetPendingNonce(addr string) (uint64, error) { +func (m *MockActPool) GetPendingNonce(arg0 string) (uint64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPendingNonce", addr) + ret := m.ctrl.Call(m, "GetPendingNonce", arg0) ret0, _ := ret[0].(uint64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetPendingNonce indicates an expected call of GetPendingNonce. -func (mr *MockActPoolMockRecorder) GetPendingNonce(addr interface{}) *gomock.Call { +func (mr *MockActPoolMockRecorder) GetPendingNonce(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingNonce", reflect.TypeOf((*MockActPool)(nil).GetPendingNonce), addr) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingNonce", reflect.TypeOf((*MockActPool)(nil).GetPendingNonce), arg0) } // GetSize mocks base method. @@ -167,17 +180,17 @@ func (mr *MockActPoolMockRecorder) GetSize() *gomock.Call { } // GetUnconfirmedActs mocks base method. -func (m *MockActPool) GetUnconfirmedActs(addr string) []*action.SealedEnvelope { +func (m *MockActPool) GetUnconfirmedActs(arg0 string) []*action.SealedEnvelope { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUnconfirmedActs", addr) + ret := m.ctrl.Call(m, "GetUnconfirmedActs", arg0) ret0, _ := ret[0].([]*action.SealedEnvelope) return ret0 } // GetUnconfirmedActs indicates an expected call of GetUnconfirmedActs. -func (mr *MockActPoolMockRecorder) GetUnconfirmedActs(addr interface{}) *gomock.Call { +func (mr *MockActPoolMockRecorder) GetUnconfirmedActs(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnconfirmedActs", reflect.TypeOf((*MockActPool)(nil).GetUnconfirmedActs), addr) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnconfirmedActs", reflect.TypeOf((*MockActPool)(nil).GetUnconfirmedActs), arg0) } // PendingActionMap mocks base method. @@ -261,50 +274,3 @@ func (mr *MockActPoolMockRecorder) Validate(arg0, arg1 interface{}) *gomock.Call mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockActPool)(nil).Validate), arg0, arg1) } - -// MockSubscriber is a mock of Subscriber interface. -type MockSubscriber struct { - ctrl *gomock.Controller - recorder *MockSubscriberMockRecorder -} - -// MockSubscriberMockRecorder is the mock recorder for MockSubscriber. -type MockSubscriberMockRecorder struct { - mock *MockSubscriber -} - -// NewMockSubscriber creates a new mock instance. -func NewMockSubscriber(ctrl *gomock.Controller) *MockSubscriber { - mock := &MockSubscriber{ctrl: ctrl} - mock.recorder = &MockSubscriberMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockSubscriber) EXPECT() *MockSubscriberMockRecorder { - return m.recorder -} - -// OnAdded mocks base method. -func (m *MockSubscriber) OnAdded(arg0 *action.SealedEnvelope) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "OnAdded", arg0) -} - -// OnAdded indicates an expected call of OnAdded. -func (mr *MockSubscriberMockRecorder) OnAdded(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnAdded", reflect.TypeOf((*MockSubscriber)(nil).OnAdded), arg0) -} - -// OnRemoved mocks base method. -func (m *MockSubscriber) OnRemoved(arg0 *action.SealedEnvelope) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "OnRemoved", arg0) -} - -// OnRemoved indicates an expected call of OnRemoved. -func (mr *MockSubscriberMockRecorder) OnRemoved(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnRemoved", reflect.TypeOf((*MockSubscriber)(nil).OnRemoved), arg0) -} diff --git a/test/mock/mock_blockdao/mock_blockindexer.go b/test/mock/mock_blockdao/mock_blockindexer.go index 14d2b35096..b9ce6c019f 100644 --- a/test/mock/mock_blockdao/mock_blockindexer.go +++ b/test/mock/mock_blockdao/mock_blockindexer.go @@ -35,20 +35,6 @@ func (m *MockBlockIndexer) EXPECT() *MockBlockIndexerMockRecorder { return m.recorder } -// DeleteTipBlock mocks base method. -func (m *MockBlockIndexer) DeleteTipBlock(arg0 context.Context, arg1 *block.Block) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteTipBlock", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteTipBlock indicates an expected call of DeleteTipBlock. -func (mr *MockBlockIndexerMockRecorder) DeleteTipBlock(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTipBlock", reflect.TypeOf((*MockBlockIndexer)(nil).DeleteTipBlock), arg0, arg1) -} - // Height mocks base method. func (m *MockBlockIndexer) Height() (uint64, error) { m.ctrl.T.Helper() diff --git a/test/mock/mock_blockdao/mock_blockindexer_withstart.go b/test/mock/mock_blockdao/mock_blockindexer_withstart.go index 2fe01a3da5..25fc7848d6 100644 --- a/test/mock/mock_blockdao/mock_blockindexer_withstart.go +++ b/test/mock/mock_blockdao/mock_blockindexer_withstart.go @@ -35,20 +35,6 @@ func (m *MockBlockIndexerWithStart) EXPECT() *MockBlockIndexerWithStartMockRecor return m.recorder } -// DeleteTipBlock mocks base method. -func (m *MockBlockIndexerWithStart) DeleteTipBlock(arg0 context.Context, arg1 *block.Block) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteTipBlock", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteTipBlock indicates an expected call of DeleteTipBlock. -func (mr *MockBlockIndexerWithStartMockRecorder) DeleteTipBlock(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTipBlock", reflect.TypeOf((*MockBlockIndexerWithStart)(nil).DeleteTipBlock), arg0, arg1) -} - // Height mocks base method. func (m *MockBlockIndexerWithStart) Height() (uint64, error) { m.ctrl.T.Helper() diff --git a/test/mock/mock_blockindex/mock_blockindex.go b/test/mock/mock_blockindex/mock_blockindex.go index efd3f7bece..8fcd9ae8d4 100644 --- a/test/mock/mock_blockindex/mock_blockindex.go +++ b/test/mock/mock_blockindex/mock_blockindex.go @@ -52,20 +52,6 @@ func (mr *MockBloomFilterIndexerMockRecorder) BlockFilterByHeight(arg0 interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BlockFilterByHeight", reflect.TypeOf((*MockBloomFilterIndexer)(nil).BlockFilterByHeight), arg0) } -// DeleteTipBlock mocks base method. -func (m *MockBloomFilterIndexer) DeleteTipBlock(arg0 context.Context, arg1 *block.Block) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteTipBlock", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteTipBlock indicates an expected call of DeleteTipBlock. -func (mr *MockBloomFilterIndexerMockRecorder) DeleteTipBlock(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTipBlock", reflect.TypeOf((*MockBloomFilterIndexer)(nil).DeleteTipBlock), arg0, arg1) -} - // FilterBlocksInRange mocks base method. func (m *MockBloomFilterIndexer) FilterBlocksInRange(arg0 *logfilter.LogFilter, arg1, arg2, arg3 uint64) ([]uint64, error) { m.ctrl.T.Helper() diff --git a/test/mock/mock_blockindex/mock_indexer.go b/test/mock/mock_blockindex/mock_indexer.go index e564b3df0b..a763b098a8 100644 --- a/test/mock/mock_blockindex/mock_indexer.go +++ b/test/mock/mock_blockindex/mock_indexer.go @@ -37,20 +37,6 @@ func (m *MockIndexer) EXPECT() *MockIndexerMockRecorder { return m.recorder } -// DeleteTipBlock mocks base method. -func (m *MockIndexer) DeleteTipBlock(arg0 context.Context, arg1 *block.Block) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteTipBlock", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteTipBlock indicates an expected call of DeleteTipBlock. -func (mr *MockIndexerMockRecorder) DeleteTipBlock(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTipBlock", reflect.TypeOf((*MockIndexer)(nil).DeleteTipBlock), arg0, arg1) -} - // GetActionCountByAddress mocks base method. func (m *MockIndexer) GetActionCountByAddress(arg0 hash.Hash160) (uint64, error) { m.ctrl.T.Helper() diff --git a/test/mock/mock_envelope/mock_envelope.go b/test/mock/mock_envelope/mock_envelope.go index 945ec8cc0a..daf7bda2d9 100644 --- a/test/mock/mock_envelope/mock_envelope.go +++ b/test/mock/mock_envelope/mock_envelope.go @@ -1158,6 +1158,20 @@ func (m *MockTxDynamicGas) EXPECT() *MockTxDynamicGasMockRecorder { return m.recorder } +// EffectiveGasPrice mocks base method. +func (m *MockTxDynamicGas) EffectiveGasPrice(arg0 *big.Int) *big.Int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EffectiveGasPrice", arg0) + ret0, _ := ret[0].(*big.Int) + return ret0 +} + +// EffectiveGasPrice indicates an expected call of EffectiveGasPrice. +func (mr *MockTxDynamicGasMockRecorder) EffectiveGasPrice(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EffectiveGasPrice", reflect.TypeOf((*MockTxDynamicGas)(nil).EffectiveGasPrice), arg0) +} + // GasFeeCap mocks base method. func (m *MockTxDynamicGas) GasFeeCap() *big.Int { m.ctrl.T.Helper() diff --git a/test/mock/mock_factory/mock_factory.go b/test/mock/mock_factory/mock_factory.go index 4e5f71671e..475f831700 100644 --- a/test/mock/mock_factory/mock_factory.go +++ b/test/mock/mock_factory/mock_factory.go @@ -39,20 +39,6 @@ func (m *MockFactory) EXPECT() *MockFactoryMockRecorder { return m.recorder } -// DeleteTipBlock mocks base method. -func (m *MockFactory) DeleteTipBlock(arg0 context.Context, arg1 *block.Block) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteTipBlock", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteTipBlock indicates an expected call of DeleteTipBlock. -func (mr *MockFactoryMockRecorder) DeleteTipBlock(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTipBlock", reflect.TypeOf((*MockFactory)(nil).DeleteTipBlock), arg0, arg1) -} - // Height mocks base method. func (m *MockFactory) Height() (uint64, error) { m.ctrl.T.Helper() @@ -160,25 +146,6 @@ func (mr *MockFactoryMockRecorder) State(arg0 interface{}, arg1 ...interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "State", reflect.TypeOf((*MockFactory)(nil).State), varargs...) } -// StateAtHeight mocks base method. -func (m *MockFactory) StateAtHeight(arg0 uint64, arg1 interface{}, arg2 ...protocol.StateOption) error { - m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "StateAtHeight", varargs...) - ret0, _ := ret[0].(error) - return ret0 -} - -// StateAtHeight indicates an expected call of StateAtHeight. -func (mr *MockFactoryMockRecorder) StateAtHeight(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateAtHeight", reflect.TypeOf((*MockFactory)(nil).StateAtHeight), varargs...) -} - // States mocks base method. func (m *MockFactory) States(arg0 ...protocol.StateOption) (uint64, state.Iterator, error) { m.ctrl.T.Helper() @@ -199,26 +166,6 @@ func (mr *MockFactoryMockRecorder) States(arg0 ...interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "States", reflect.TypeOf((*MockFactory)(nil).States), arg0...) } -// StatesAtHeight mocks base method. -func (m *MockFactory) StatesAtHeight(arg0 uint64, arg1 ...protocol.StateOption) (state.Iterator, error) { - m.ctrl.T.Helper() - varargs := []interface{}{arg0} - for _, a := range arg1 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "StatesAtHeight", varargs...) - ret0, _ := ret[0].(state.Iterator) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// StatesAtHeight indicates an expected call of StatesAtHeight. -func (mr *MockFactoryMockRecorder) StatesAtHeight(arg0 interface{}, arg1 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0}, arg1...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StatesAtHeight", reflect.TypeOf((*MockFactory)(nil).StatesAtHeight), varargs...) -} - // Stop mocks base method. func (m *MockFactory) Stop(arg0 context.Context) error { m.ctrl.T.Helper() diff --git a/tools/actioninjector.v2/internal/client/client_test.go b/tools/actioninjector.v2/internal/client/client_test.go index 6a499a540b..28eeab7697 100644 --- a/tools/actioninjector.v2/internal/client/client_test.go +++ b/tools/actioninjector.v2/internal/client/client_test.go @@ -76,6 +76,7 @@ func TestClient(t *testing.T) { bc.EXPECT().BlockHeaderByHeight(gomock.Any()).Return(&blh, nil).AnyTimes() ap.EXPECT().GetPendingNonce(gomock.Any()).Return(uint64(1), nil).AnyTimes() ap.EXPECT().Add(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + ap.EXPECT().AddSubscriber(gomock.Any()).AnyTimes() newOption := api.WithBroadcastOutbound(func(_ context.Context, _ uint32, _ proto.Message) error { return nil })