diff --git a/block/factory/factory.go b/block/factory/factory.go index 4d540e0cd..dcef0c142 100644 --- a/block/factory/factory.go +++ b/block/factory/factory.go @@ -8,6 +8,7 @@ import ( v0 "github.com/harmony-one/harmony/block/v0" v1 "github.com/harmony-one/harmony/block/v1" v2 "github.com/harmony-one/harmony/block/v2" + v3 "github.com/harmony-one/harmony/block/v3" "github.com/harmony-one/harmony/internal/params" ) @@ -29,6 +30,8 @@ func NewFactory(chainConfig *params.ChainConfig) Factory { func (f *factory) NewHeader(epoch *big.Int) *block.Header { var impl blockif.Header switch { + case epoch.Cmp(f.chainConfig.StakingEpoch) >= 0: + impl = v3.NewHeader() case epoch.Cmp(f.chainConfig.CrossLinkEpoch) >= 0: impl = v2.NewHeader() case epoch.Cmp(f.chainConfig.CrossTxEpoch) >= 0: diff --git a/block/v3/header.go b/block/v3/header.go new file mode 100644 index 000000000..5f417addb --- /dev/null +++ b/block/v3/header.go @@ -0,0 +1,16 @@ +package v3 + +import ( + "github.com/harmony-one/harmony/block/v2" +) + +// Header v3 has the same structure as v2 header +// It is used to identify the body v3 which including staking txs +type Header struct { + v2.Header +} + +// NewHeader creates a new header object. +func NewHeader() *Header { + return &Header{*v2.NewHeader()} +} diff --git a/consensus/engine/consensus_engine.go b/consensus/engine/consensus_engine.go index f6c6cecb5..5e316a353 100644 --- a/consensus/engine/consensus_engine.go +++ b/consensus/engine/consensus_engine.go @@ -80,9 +80,8 @@ type Engine interface { Finalize( chain ChainReader, header *block.Header, state *state.DB, txs []*types.Transaction, - stkgTxs []*staking.StakingTransaction, receipts []*types.Receipt, outcxs []*types.CXReceipt, - incxs []*types.CXReceiptsProof) (*types.Block, error) + incxs []*types.CXReceiptsProof, stks []*staking.StakingTransaction) (*types.Block, error) // Seal generates a new sealing request for the given input block and pushes // the result into the given channel. diff --git a/core/blockchain.go b/core/blockchain.go index 1aaaaed73..ac5aadc8f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2252,11 +2252,11 @@ func (bc *BlockChain) ReadTxLookupEntry(txID common.Hash) (common.Hash, uint64, return rawdb.ReadTxLookupEntry(bc.db, txID) } -// ReadStakingValidator reads staking information of given validator -func (bc *BlockChain) ReadStakingValidator(addr common.Address) (*staking.Validator, error) { +// ReadStakingValidator reads staking information of given validatorWrapper +func (bc *BlockChain) ReadStakingValidator(addr common.Address) (*staking.ValidatorWrapper, error) { if cached, ok := bc.stakingCache.Get("staking-" + string(addr.Bytes())); ok { by := cached.([]byte) - v := staking.Validator{} + v := staking.ValidatorWrapper{} if err := rlp.DecodeBytes(by, &v); err != nil { return nil, err } @@ -2266,8 +2266,8 @@ func (bc *BlockChain) ReadStakingValidator(addr common.Address) (*staking.Valida return rawdb.ReadStakingValidator(bc.db, addr) } -// WriteStakingValidator reads staking information of given validator -func (bc *BlockChain) WriteStakingValidator(v *staking.Validator) error { +// WriteStakingValidator reads staking information of given validatorWrapper +func (bc *BlockChain) WriteStakingValidator(v *staking.ValidatorWrapper) error { err := rawdb.WriteStakingValidator(bc.db, v) if err != nil { return err diff --git a/core/chain_makers.go b/core/chain_makers.go index 963f82d8a..0acfa0d2d 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -188,7 +188,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse if b.engine != nil { // Finalize and seal the block - block, err := b.engine.Finalize(chainreader, b.header, statedb, b.txs, b.stkTxs, b.receipts, nil, nil) + block, err := b.engine.Finalize(chainreader, b.header, statedb, b.txs, b.receipts, nil, nil, nil) if err != nil { panic(err) } diff --git a/core/genesis.go b/core/genesis.go index a65505ae5..9e2d3ecc0 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -266,7 +266,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { statedb.Commit(false) statedb.Database().TrieDB().Commit(root, true) - return types.NewBlock(head, nil, nil, nil, nil) + return types.NewBlock(head, nil, nil, nil, nil, nil) } // Commit writes the block and state of a genesis specification to the database. diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 80d3d4623..eb2e5cf5a 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -615,13 +615,13 @@ func WriteCXReceiptsProofUnspentCheckpoint(db DatabaseWriter, shardID uint32, bl } // ReadStakingValidator retrieves staking validator by its address -func ReadStakingValidator(db DatabaseReader, addr common.Address) (*staking.Validator, error) { +func ReadStakingValidator(db DatabaseReader, addr common.Address) (*staking.ValidatorWrapper, error) { data, err := db.Get(stakingKey(addr)) if len(data) == 0 || err != nil { utils.Logger().Info().Err(err).Msg("ReadStakingValidator") return nil, err } - v := staking.Validator{} + v := staking.ValidatorWrapper{} if err := rlp.DecodeBytes(data, &v); err != nil { utils.Logger().Error().Err(err).Str("address", addr.Hex()).Msg("Unable to Decode staking validator from database") return nil, err @@ -630,7 +630,7 @@ func ReadStakingValidator(db DatabaseReader, addr common.Address) (*staking.Vali } // WriteStakingValidator stores staking validator's information by its address -func WriteStakingValidator(db DatabaseWriter, v *staking.Validator) error { +func WriteStakingValidator(db DatabaseWriter, v *staking.ValidatorWrapper) error { bytes, err := rlp.EncodeToBytes(v) if err != nil { utils.Logger().Error().Msg("[WriteStakingValidator] Failed to encode") diff --git a/core/state_processor.go b/core/state_processor.go index 318c79959..354a87610 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -94,7 +94,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.DB, cfg vm.C } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) - _, err := p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.StakingTransactions(), receipts, outcxs, incxs) + _, err := p.engine.Finalize(p.bc, header, statedb, block.Transactions(), receipts, outcxs, incxs, block.StakingTransactions()) if err != nil { return nil, nil, nil, 0, ctxerror.New("cannot finalize block").WithCause(err) } diff --git a/core/types/block.go b/core/types/block.go index 2b776982b..bb180759f 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -35,6 +35,7 @@ import ( v0 "github.com/harmony-one/harmony/block/v0" v1 "github.com/harmony-one/harmony/block/v1" v2 "github.com/harmony-one/harmony/block/v2" + v3 "github.com/harmony-one/harmony/block/v3" "github.com/harmony-one/harmony/crypto/hash" "github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/shard" @@ -124,6 +125,8 @@ type Body struct { func NewBodyForMatchingHeader(h *block.Header) (*Body, error) { var bi BodyInterface switch h.Header.(type) { + case *v3.Header: + bi = new(BodyV2) case *v2.Header, *v1.Header: bi = new(BodyV1) case *v0.Header: @@ -186,6 +189,7 @@ type Block struct { header *block.Header uncles []*block.Header transactions Transactions + stks staking.StakingTransactions incomingReceipts CXReceiptsProofs // caches @@ -239,11 +243,21 @@ type extblockV1 struct { IncomingReceipts CXReceiptsProofs } +// includes staking transaction +type extblockV2 struct { + Header *block.Header + Txs []*Transaction + Stks []*staking.StakingTransaction + Uncles []*block.Header + IncomingReceipts CXReceiptsProofs +} + var extblockReg = taggedrlp.NewRegistry() func init() { extblockReg.MustRegister(taggedrlp.LegacyTag, &extblock{}) extblockReg.MustRegister("v1", &extblockV1{}) + extblockReg.MustRegister("v2", &extblockV2{}) } // NewBlock creates a new block. The input data is copied, @@ -253,7 +267,7 @@ func init() { // The values of TxHash, UncleHash, ReceiptHash and Bloom in header // are ignored and set to values derived from the given txs, // and receipts. -func NewBlock(header *block.Header, txs []*Transaction, receipts []*Receipt, outcxs []*CXReceipt, incxs []*CXReceiptsProof) *Block { +func NewBlock(header *block.Header, txs []*Transaction, receipts []*Receipt, outcxs []*CXReceipt, incxs []*CXReceiptsProof, stks []*staking.StakingTransaction) *Block { b := &Block{header: CopyHeader(header)} // TODO: panic if len(txs) != len(receipts) @@ -282,6 +296,11 @@ func NewBlock(header *block.Header, txs []*Transaction, receipts []*Receipt, out copy(b.incomingReceipts, incxs) } + if len(stks) > 0 { + b.stks = make(staking.StakingTransactions, len(stks)) + copy(b.stks, stks) + } + return b } @@ -308,6 +327,8 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error { return err } switch eb := eb.(type) { + case *extblockV2: + b.header, b.uncles, b.transactions, b.incomingReceipts, b.stks = eb.Header, eb.Uncles, eb.Txs, eb.IncomingReceipts, eb.Stks case *extblockV1: b.header, b.uncles, b.transactions, b.incomingReceipts = eb.Header, eb.Uncles, eb.Txs, eb.IncomingReceipts case *extblock: @@ -323,6 +344,8 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error { func (b *Block) EncodeRLP(w io.Writer) error { var eb interface{} switch h := b.header.Header.(type) { + case *v3.Header: + eb = extblockV2{b.header, b.transactions, b.stks, b.uncles, b.incomingReceipts} case *v2.Header, *v1.Header: eb = extblockV1{b.header, b.transactions, b.uncles, b.incomingReceipts} case *v0.Header: @@ -349,7 +372,7 @@ func (b *Block) Transactions() Transactions { // StakingTransactions returns stakingTransactions. func (b *Block) StakingTransactions() staking.StakingTransactions { - return staking.StakingTransactions{} + return b.stks } // IncomingReceipts returns verified outgoing receipts diff --git a/core/types/bodyv2.go b/core/types/bodyv2.go new file mode 100644 index 000000000..1723e913c --- /dev/null +++ b/core/types/bodyv2.go @@ -0,0 +1,128 @@ +package types + +import ( + "io" + + "github.com/ethereum/go-ethereum/rlp" + + "github.com/harmony-one/harmony/block" + staking "github.com/harmony-one/harmony/staking/types" +) + +// BodyV2 is the V2 block body +type BodyV2 struct { + f bodyFieldsV2 +} + +type bodyFieldsV2 struct { + Transactions []*Transaction + StakingTransactions []*staking.StakingTransaction + Uncles []*block.Header + IncomingReceipts CXReceiptsProofs +} + +// Transactions returns the list of transactions. +// +// The returned list is a deep copy; the caller may do anything with it without +// affecting the original. +func (b *BodyV2) Transactions() (txs []*Transaction) { + for _, tx := range b.f.Transactions { + txs = append(txs, tx.Copy()) + } + return txs +} + +// StakingTransactions returns the list of staking transactions. +// The returned list is a deep copy; the caller may do anything with it without +// affecting the original. +func (b *BodyV2) StakingTransactions() (txs []*staking.StakingTransaction) { + for _, tx := range b.f.StakingTransactions { + txs = append(txs, tx.Copy()) + } + return txs +} + +// TransactionAt returns the transaction at the given index in this block. +// It returns nil if index is out of bounds. +func (b *BodyV2) TransactionAt(index int) *Transaction { + if index < 0 || index >= len(b.f.Transactions) { + return nil + } + return b.f.Transactions[index].Copy() +} + +// CXReceiptAt returns the CXReceipt at given index in this block +// It returns nil if index is out of bounds +func (b *BodyV2) CXReceiptAt(index int) *CXReceipt { + if index < 0 { + return nil + } + for _, cxp := range b.f.IncomingReceipts { + cxs := cxp.Receipts + if index < len(cxs) { + return cxs[index].Copy() + } + index -= len(cxs) + } + return nil +} + +// SetTransactions sets the list of transactions with a deep copy of the given +// list. +func (b *BodyV2) SetTransactions(newTransactions []*Transaction) { + var txs []*Transaction + for _, tx := range newTransactions { + txs = append(txs, tx.Copy()) + } + b.f.Transactions = txs +} + +// SetStakingTransactions sets the list of staking transactions with a deep copy of the given +// list. +func (b *BodyV2) SetStakingTransactions(newStakingTransactions []*staking.StakingTransaction) { + var txs []*staking.StakingTransaction + for _, tx := range newStakingTransactions { + txs = append(txs, tx.Copy()) + } + b.f.StakingTransactions = txs +} + +// Uncles returns a deep copy of the list of uncle headers of this block. +func (b *BodyV2) Uncles() (uncles []*block.Header) { + for _, uncle := range b.f.Uncles { + uncles = append(uncles, CopyHeader(uncle)) + } + return uncles +} + +// SetUncles sets the list of uncle headers with a deep copy of the given list. +func (b *BodyV2) SetUncles(newUncle []*block.Header) { + var uncles []*block.Header + for _, uncle := range newUncle { + uncles = append(uncles, CopyHeader(uncle)) + } + b.f.Uncles = uncles +} + +// IncomingReceipts returns a deep copy of the list of incoming cross-shard +// transaction receipts of this block. +func (b *BodyV2) IncomingReceipts() (incomingReceipts CXReceiptsProofs) { + return b.f.IncomingReceipts.Copy() +} + +// SetIncomingReceipts sets the list of incoming cross-shard transaction +// receipts of this block with a dep copy of the given list. +func (b *BodyV2) SetIncomingReceipts(newIncomingReceipts CXReceiptsProofs) { + b.f.IncomingReceipts = newIncomingReceipts.Copy() +} + +// EncodeRLP RLP-encodes the block body into the given writer. +func (b *BodyV2) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, &b.f) +} + +// DecodeRLP RLP-decodes a block body from the given RLP stream into the +// receiver. +func (b *BodyV2) DecodeRLP(s *rlp.Stream) error { + return s.Decode(&b.f) +} diff --git a/internal/chain/engine.go b/internal/chain/engine.go index 1c16309d0..c9f0d9306 100644 --- a/internal/chain/engine.go +++ b/internal/chain/engine.go @@ -151,16 +151,15 @@ func (e *engineImpl) VerifySeal(chain engine.ChainReader, header *block.Header) // setting the final state and assembling the block. func (e *engineImpl) Finalize( chain engine.ChainReader, header *block.Header, state *state.DB, txs []*types.Transaction, - stkgTxs []*staking.StakingTransaction, receipts []*types.Receipt, outcxs []*types.CXReceipt, - incxs []*types.CXReceiptsProof) (*types.Block, error) { + incxs []*types.CXReceiptsProof, stks []*staking.StakingTransaction) (*types.Block, error) { // Accumulate any block and uncle rewards and commit the final state root // Header seems complete, assemble into a block and return if err := AccumulateRewards(chain, state, header); err != nil { return nil, ctxerror.New("cannot pay block reward").WithCause(err) } header.SetRoot(state.IntermediateRoot(chain.Config().IsS3(header.Epoch()))) - return types.NewBlock(header, txs, receipts, outcxs, incxs), nil + return types.NewBlock(header, txs, receipts, outcxs, incxs, stks), nil } // QuorumForBlock returns the quorum for the given block header. diff --git a/internal/params/config.go b/internal/params/config.go index dc463172d..ef747050b 100644 --- a/internal/params/config.go +++ b/internal/params/config.go @@ -26,6 +26,7 @@ var ( ChainID: MainnetChainID, CrossTxEpoch: big.NewInt(28), CrossLinkEpoch: EpochTBD, + StakingEpoch: EpochTBD, EIP155Epoch: big.NewInt(28), S3Epoch: big.NewInt(28), } @@ -35,6 +36,7 @@ var ( ChainID: TestnetChainID, CrossTxEpoch: big.NewInt(1), CrossLinkEpoch: big.NewInt(2), + StakingEpoch: big.NewInt(3), EIP155Epoch: big.NewInt(0), S3Epoch: big.NewInt(0), } @@ -45,6 +47,7 @@ var ( ChainID: PangaeaChainID, CrossTxEpoch: big.NewInt(0), CrossLinkEpoch: EpochTBD, + StakingEpoch: EpochTBD, EIP155Epoch: big.NewInt(0), S3Epoch: big.NewInt(0), } @@ -56,6 +59,7 @@ var ( AllProtocolChangesChainID, // ChainID big.NewInt(0), // CrossTxEpoch big.NewInt(0), // CrossLinkEpoch + big.NewInt(0), // StakingEpoch big.NewInt(0), // EIP155Epoch big.NewInt(0), // S3Epoch } @@ -67,6 +71,7 @@ var ( TestChainID, // ChainID big.NewInt(0), // CrossTxEpoch big.NewInt(0), // CrossLinkEpoch + big.NewInt(0), // StakingEpoch big.NewInt(0), // EIP155Epoch big.NewInt(0), // S3Epoch } @@ -103,16 +108,20 @@ type ChainConfig struct { // cross-shard links. CrossLinkEpoch *big.Int `json:"crossLinkEpoch,omitempty"` + // StakingEpoch is the epoch we allow staking transactions + StakingEpoch *big.Int `json:"stakingEpoch,omitempty"` + EIP155Epoch *big.Int `json:"eip155Epoch,omitempty"` // EIP155 hard fork epoch (include EIP158 too) S3Epoch *big.Int `json:"s3Epoch,omitempty"` // S3 epoch is the first epoch containing S3 mainnet and all ethereum update up to Constantinople } // String implements the fmt.Stringer interface. func (c *ChainConfig) String() string { - return fmt.Sprintf("{ChainID: %v EIP155: %v CrossTx: %v CrossLink: %v}", + return fmt.Sprintf("{ChainID: %v EIP155: %v CrossTx: %v Staking: %v CrossLink: %v}", c.ChainID, c.EIP155Epoch, c.CrossTxEpoch, + c.StakingEpoch, c.CrossLinkEpoch, ) } @@ -137,6 +146,12 @@ func (c *ChainConfig) IsCrossTx(epoch *big.Int) bool { return isForked(crossTxEpoch, epoch) } +// IsStaking determines whether it is staking epoch +func (c *ChainConfig) IsStaking(epoch *big.Int) bool { + stkEpoch := new(big.Int).Add(c.StakingEpoch, common.Big1) + return isForked(stkEpoch, epoch) +} + // IsCrossLink returns whether epoch is either equal to the CrossLink fork epoch or greater. func (c *ChainConfig) IsCrossLink(epoch *big.Int) bool { return isForked(c.CrossLinkEpoch, epoch) diff --git a/node/node.go b/node/node.go index c58a24b9c..e8041ac4d 100644 --- a/node/node.go +++ b/node/node.go @@ -347,7 +347,7 @@ func (node *Node) AddPendingReceipts(receipts *types.CXReceiptsProof) { // Note the pending transaction list will then contain the rest of the txs func (node *Node) getTransactionsForNewBlock( coinbase common.Address, -) (types.Transactions, staking.StakingTransactions) { +) types.Transactions { txsThrottleConfig := core.ShardingSchedule.TxsThrottleConfig() @@ -368,7 +368,7 @@ func (node *Node) getTransactionsForNewBlock( utils.Logger().Error(). Err(err). Msg("Failed updating worker's state before txn selection") - return types.Transactions{}, staking.StakingTransactions{} + return types.Transactions{} } node.pendingTxMutex.Lock() @@ -387,8 +387,6 @@ func (node *Node) getTransactionsForNewBlock( } selected, unselected, invalid := node.Worker.SelectTransactionsForNewBlock(newBlockNum, pendingTransactions, node.recentTxsStats, txsThrottleConfig, coinbase) - selectedStaking, unselectedStaking, invalidStaking := - node.Worker.SelectStakingTransactionsForNewBlock(newBlockNum, pendingStakingTransactions, coinbase) node.pendingTransactions = make(map[common.Hash]*types.Transaction) for _, unselectedTx := range unselected { @@ -400,17 +398,7 @@ func (node *Node) getTransactionsForNewBlock( Int("invalidDiscarded", len(invalid)). Msg("Selecting Transactions") - node.pendingStakingTransactions = make(map[common.Hash]*staking.StakingTransaction) - for _, unselectedStakingTx := range unselectedStaking { - node.pendingStakingTransactions[unselectedStakingTx.Hash()] = unselectedStakingTx - } - utils.Logger().Info(). - Int("remainPending", len(node.pendingStakingTransactions)). - Int("selected", len(unselectedStaking)). - Int("invalidDiscarded", len(invalidStaking)). - Msg("Selecting Transactions") - - return selected, selectedStaking + return selected } func (node *Node) startRxPipeline( diff --git a/node/node_newblock.go b/node/node_newblock.go index 711cb7225..dd7e1f81e 100644 --- a/node/node_newblock.go +++ b/node/node_newblock.go @@ -81,9 +81,9 @@ func (node *Node) proposeNewBlock() (*types.Block, error) { coinbase := node.Consensus.SelfAddress // Prepare transactions including staking transactions - selectedTxs, selectedStakingTxs := node.getTransactionsForNewBlock(coinbase) + selectedTxs := node.getTransactionsForNewBlock(coinbase) - if err := node.Worker.CommitTransactions(selectedTxs, selectedStakingTxs, coinbase); err != nil { + if err := node.Worker.CommitTransactions(selectedTxs, coinbase); err != nil { ctxerror.Log15(utils.GetLogger().Error, ctxerror.New("cannot commit transactions"). WithCause(err)) diff --git a/node/worker/worker.go b/node/worker/worker.go index b66005b7a..4d53fe3bf 100644 --- a/node/worker/worker.go +++ b/node/worker/worker.go @@ -13,7 +13,6 @@ import ( "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/types" - "github.com/harmony-one/harmony/core/values" "github.com/harmony-one/harmony/core/vm" shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding" "github.com/harmony-one/harmony/internal/ctxerror" @@ -145,63 +144,6 @@ func (w *Worker) SelectTransactionsForNewBlock(newBlockNum uint64, txs types.Tra return selected, unselected, invalid } -// SelectStakingTransactionsForNewBlock selects staking transactions for new block. -func (w *Worker) SelectStakingTransactionsForNewBlock( - newBlockNum uint64, txs staking.StakingTransactions, - coinbase common.Address) (staking.StakingTransactions, staking.StakingTransactions, staking.StakingTransactions) { - - // only beaconchain process staking transaction - if w.chain.ShardID() != values.BeaconChainShardID { - return nil, nil, nil - } - - // staking transaction share the same gasPool with normal transactions - if w.current.gasPool == nil { - w.current.gasPool = new(core.GasPool).AddGas(w.current.header.GasLimit()) - } - - selected := staking.StakingTransactions{} - unselected := staking.StakingTransactions{} - invalid := staking.StakingTransactions{} - for _, tx := range txs { - snap := w.current.state.Snapshot() - _, err := w.commitStakingTransaction(tx, coinbase) - if err != nil { - w.current.state.RevertToSnapshot(snap) - invalid = append(invalid, tx) - utils.Logger().Error().Err(err).Str("stakingTxId", tx.Hash().Hex()).Msg("Commit staking transaction error") - } else { - selected = append(selected, tx) - utils.Logger().Info().Str("stakingTxId", tx.Hash().Hex()).Uint64("txGasLimit", tx.Gas()).Msg("StakingTransaction gas limit info") - } - } - - utils.Logger().Info().Uint64("newBlockNum", newBlockNum).Uint64("blockGasLimit", - w.current.header.GasLimit()).Uint64("blockGasUsed", - w.current.header.GasUsed()).Msg("[SelectStakingTransaction] Block gas limit and usage info") - - return selected, unselected, invalid - -} - -func (w *Worker) commitStakingTransaction(tx *staking.StakingTransaction, coinbase common.Address) ([]*types.Log, error) { - snap := w.current.state.Snapshot() - gasUsed := w.current.header.GasUsed() - receipt, _, err := - core.ApplyStakingTransaction(w.config, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &gasUsed, vm.Config{}) - w.current.header.SetGasUsed(gasUsed) - if err != nil { - w.current.state.RevertToSnapshot(snap) - return nil, err - } - if receipt == nil { - return nil, fmt.Errorf("nil staking receipt") - } - w.current.stkingTxs = append(w.current.stkingTxs, tx) - w.current.receipts = append(w.current.receipts, receipt) - return receipt.Logs, nil -} - func (w *Worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { snap := w.current.state.Snapshot() @@ -227,7 +169,7 @@ func (w *Worker) commitTransaction(tx *types.Transaction, coinbase common.Addres // CommitTransactions commits transactions including staking transactions. func (w *Worker) CommitTransactions( - txs types.Transactions, stakingTxns staking.StakingTransactions, coinbase common.Address) error { + txs types.Transactions, coinbase common.Address) error { // Must update to the correct current state before processing potential txns if err := w.UpdateCurrent(coinbase); err != nil { utils.Logger().Error(). @@ -248,10 +190,6 @@ func (w *Worker) CommitTransactions( } } - for _, stakingTx := range stakingTxns { - _ = stakingTx - // TODO: add logic to commit staking txns - } return nil } @@ -393,11 +331,14 @@ func (w *Worker) FinalizeNewBlock(sig []byte, signers []byte, viewID uint64, coi } } + stks, _, err := w.UpdateStakeInformation() + s := w.current.state.Copy() copyHeader := types.CopyHeader(w.current.header) block, err := w.engine.Finalize( - w.chain, copyHeader, s, w.current.txs, w.current.stkingTxs, w.current.receipts, w.current.outcxs, w.current.incxs, + w.chain, copyHeader, s, w.current.txs, w.current.receipts, w.current.outcxs, w.current.incxs, + stks, ) if err != nil { return nil, ctxerror.New("cannot finalize block").WithCause(err) @@ -405,6 +346,25 @@ func (w *Worker) FinalizeNewBlock(sig []byte, signers []byte, viewID uint64, coi return block, nil } +// UpdateStakeInformation updates validator and its delegation information +func (w *Worker) UpdateStakeInformation() ([]*staking.StakingTransaction, []*staking.ValidatorWrapper, error) { + // node.pendingStakingTransactions = make(map[common.Hash]*staking.StakingTransaction) + // for _, unselectedStakingTx := range unselectedStaking { + // node.pendingStakingTransactions[unselectedStakingTx.Hash()] = unselectedStakingTx + // } + + addrs, err := w.chain.ReadValidatorList() + if err != nil { + return nil, nil, err + } + //validatorInfo := []staking.ValidatorWrapper{} + for _, addr := range addrs { + _ = addr + } + + return nil, nil, nil +} + // New create a new worker object. func New(config *params.ChainConfig, chain *core.BlockChain, engine consensus_engine.Engine) *Worker { worker := &Worker{ diff --git a/staking/types/transaction.go b/staking/types/transaction.go index 4cac5af32..e20633d2a 100644 --- a/staking/types/transaction.go +++ b/staking/types/transaction.go @@ -4,6 +4,7 @@ import ( "errors" "io" "math/big" + "reflect" "sync/atomic" "github.com/ethereum/go-ethereum/common" @@ -25,6 +26,26 @@ type txdata struct { Hash *common.Hash `json:"hash" rlp:"-"` } +func (d *txdata) CopyFrom(d2 *txdata) { + d.Directive = d2.Directive + d.AccountNonce = d2.AccountNonce + d.Price = new(big.Int).Set(d2.Price) + d.GasLimit = d2.GasLimit + d.StakeMsg = reflect.New(reflect.ValueOf(d2.StakeMsg).Elem().Type()).Interface() + d.V = new(big.Int).Set(d2.V) + d.R = new(big.Int).Set(d2.R) + d.S = new(big.Int).Set(d2.S) + d.Hash = copyHash(d2.Hash) +} + +func copyHash(hash *common.Hash) *common.Hash { + if hash == nil { + return nil + } + copy := *hash + return © +} + // StakingTransaction is a record captuing all staking operations type StakingTransaction struct { data txdata @@ -76,6 +97,13 @@ func (tx *StakingTransaction) Hash() common.Hash { return v } +// Copy returns a copy of the transaction. +func (tx *StakingTransaction) Copy() *StakingTransaction { + var tx2 StakingTransaction + tx2.data.CopyFrom(&tx.data) + return &tx2 +} + // WithSignature returns a new transaction with the given signature. func (tx *StakingTransaction) WithSignature(signer Signer, sig []byte) (*StakingTransaction, error) { r, s, v, err := signer.SignatureValues(tx, sig)