Staking part2 (#1755)

* Revert "remove old design code"

This reverts commit 58448f6b23.

* fix revert conflicts

* change blspubkey type as 48 bytes for easily serialization

* WIP

* remove redelegate txType

* update delegator and validatorWrapper data structure

* fix golang test

* move txType into message creation

* remove validatorMap on statedb; move validatprMap update logic up to higher level of codes and from onchain to offchain.
pull/1764/head
chaosma 5 years ago committed by Edgar Aroutiounian
parent 0a4e4d6339
commit 942456f564
  1. 4
      consensus/engine/consensus_engine.go
  2. 57
      core/blockchain.go
  3. 22
      core/rawdb/accessors_chain.go
  4. 9
      core/state/state_object.go
  5. 45
      core/state/statedb.go
  6. 66
      core/state_processor.go
  7. 112
      core/state_transition.go
  8. 41
      core/types/transaction.go
  9. 7
      core/vm/interface.go
  10. 26
      node/node.go
  11. 8
      node/node_handler_test.go
  12. 4
      node/node_newblock.go
  13. 102
      node/worker/worker.go
  14. 2
      node/worker/worker_test.go
  15. 18
      staking/params.go
  16. 247
      staking/types/delegation.go
  17. 5
      staking/types/messages.go
  18. 50
      staking/types/validator.go
  19. 4
      staking/types/validator_test.go
  20. 8
      test/chain/main.go

@ -77,9 +77,7 @@ type Engine interface {
// and assembles the final block.
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
Finalize(
chain ChainReader, header *block.Header, state *state.DB,
txs []*types.Transaction,
Finalize(chain ChainReader, header *block.Header, state *state.DB, txs []*types.Transaction,
receipts []*types.Receipt, outcxs []*types.CXReceipt,
incxs []*types.CXReceiptsProof, stks []*staking.StakingTransaction) (*types.Block, error)

@ -69,7 +69,7 @@ const (
epochCacheLimit = 10
randomnessCacheLimit = 10
stakingCacheLimit = 256
validatorListCacheLimit = 2
validatorMapCacheLimit = 2
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
BlockChainVersion = 3
@ -133,7 +133,7 @@ type BlockChain struct {
epochCache *lru.Cache // Cache epoch number → first block number
randomnessCache *lru.Cache // Cache for vrf/vdf
stakingCache *lru.Cache // Cache for staking validator
validatorListCache *lru.Cache // Cache of validator list
validatorMapCache *lru.Cache // Cache of validator list
quit chan struct{} // blockchain quit channel
running int32 // running must be called atomically
@ -171,7 +171,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
epochCache, _ := lru.New(epochCacheLimit)
randomnessCache, _ := lru.New(randomnessCacheLimit)
stakingCache, _ := lru.New(stakingCacheLimit)
validatorListCache, _ := lru.New(validatorListCacheLimit)
validatorMapCache, _ := lru.New(validatorMapCacheLimit)
bc := &BlockChain{
chainConfig: chainConfig,
@ -191,7 +191,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
epochCache: epochCache,
randomnessCache: randomnessCache,
stakingCache: stakingCache,
validatorListCache: validatorListCache,
validatorMapCache: validatorMapCache,
engine: engine,
vmConfig: vmConfig,
badBlocks: badBlocks,
@ -2280,23 +2280,22 @@ func (bc *BlockChain) WriteStakingValidator(v *staking.ValidatorWrapper) error {
return nil
}
// ReadValidatorList reads the addresses of current all validators
func (bc *BlockChain) ReadValidatorList() ([]common.Address, error) {
if cached, ok := bc.validatorListCache.Get("validatorList"); ok {
// ReadValidatorMap reads the addresses of current all validators
func (bc *BlockChain) ReadValidatorMap() (map[common.Address]struct{}, error) {
if cached, ok := bc.validatorMapCache.Get("validatorMap"); ok {
by := cached.([]byte)
list := []common.Address{}
if err := rlp.DecodeBytes(by, &list); err != nil {
m := make(map[common.Address]struct{})
if err := rlp.DecodeBytes(by, &m); err != nil {
return nil, err
}
return list, nil
return m, nil
}
return rawdb.ReadValidatorList(bc.db)
return rawdb.ReadValidatorMap(bc.db)
}
// WriteValidatorList writes the list of validator addresses to database
func (bc *BlockChain) WriteValidatorList(addrs []common.Address) error {
err := rawdb.WriteValidatorList(bc.db, addrs)
// WriteValidatorMap writes the list of validator addresses to database
func (bc *BlockChain) WriteValidatorMap(addrs map[common.Address]struct{}) error {
err := rawdb.WriteValidatorMap(bc.db, addrs)
if err != nil {
return err
}
@ -2304,7 +2303,33 @@ func (bc *BlockChain) WriteValidatorList(addrs []common.Address) error {
if err != nil {
return err
}
bc.validatorListCache.Add("validatorList", by)
bc.validatorMapCache.Add("validatorMap", by)
return nil
}
// UpdateValidatorMap updates the validator map according to staking transaction
func (bc *BlockChain) UpdateValidatorMap(tx *staking.StakingTransaction) error {
switch tx.StakingType() {
case staking.DirectiveCreateValidator:
createValidator := tx.StakingMessage().(staking.CreateValidator)
m, err := bc.ReadValidatorMap()
if err != nil {
return err
}
if m == nil {
m = make(map[common.Address]struct{})
}
m[createValidator.ValidatorAddress] = struct{}{}
err = bc.WriteValidatorMap(m)
return err
// following cases are placeholder for now
case staking.DirectiveEditValidator:
case staking.DirectiveDelegate:
case staking.DirectiveUndelegate:
case staking.DirectiveCollectRewards:
default:
}
return nil
}

@ -641,29 +641,29 @@ func WriteStakingValidator(db DatabaseWriter, v *staking.ValidatorWrapper) error
return err
}
// ReadValidatorList retrieves staking validator by its address
func ReadValidatorList(db DatabaseReader) ([]common.Address, error) {
data, err := db.Get([]byte("validatorList"))
// ReadValidatorMap retrieves staking validator by its address
func ReadValidatorMap(db DatabaseReader) (map[common.Address]struct{}, error) {
data, err := db.Get([]byte("validatorMap"))
if len(data) == 0 || err != nil {
utils.Logger().Info().Err(err).Msg("ReadValidatorList")
utils.Logger().Info().Err(err).Msg("ReadValidatorMap")
return nil, err
}
addrs := []common.Address{}
addrs := make(map[common.Address]struct{})
if err := rlp.DecodeBytes(data, &addrs); err != nil {
utils.Logger().Error().Err(err).Msg("Unable to Decode validator List from database")
utils.Logger().Error().Err(err).Msg("Unable to Decode validator Map from database")
return nil, err
}
return addrs, nil
}
// WriteValidatorList stores staking validator's information by its address
func WriteValidatorList(db DatabaseWriter, addrs []common.Address) error {
// WriteValidatorMap stores staking validator's information by its address
func WriteValidatorMap(db DatabaseWriter, addrs map[common.Address]struct{}) error {
bytes, err := rlp.EncodeToBytes(addrs)
if err != nil {
utils.Logger().Error().Msg("[WriteValidatorList] Failed to encode")
utils.Logger().Error().Msg("[WriteValidatorMap] Failed to encode")
}
if err := db.Put([]byte("validatorList"), bytes); err != nil {
utils.Logger().Error().Msg("[WriteValidatorList] Failed to store to database")
if err := db.Put([]byte("validatorMap"), bytes); err != nil {
utils.Logger().Error().Msg("[WriteValidatorMap] Failed to store to database")
}
return err
}

@ -25,6 +25,8 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/staking"
)
var emptyCodeHash = crypto.Keccak256(nil)
@ -393,5 +395,10 @@ func (so *Object) Value() *big.Int {
}
// IsValidator checks whether it is a validator object
func (so *Object) IsValidator() {
func (so *Object) IsValidator(db Database) bool {
value := so.GetState(db, staking.IsValidatorKey)
if value == (common.Hash{}) {
return false
}
return true
}

@ -30,6 +30,8 @@ import (
"github.com/ethereum/go-ethereum/trie"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/staking"
stk "github.com/harmony-one/harmony/staking/types"
)
type revision struct {
@ -679,3 +681,46 @@ func (db *DB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
//log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
return root, err
}
// GetStakingInfo update staking information of a given validator (including delegation info)
func (db *DB) GetStakingInfo(addr common.Address) *stk.ValidatorWrapper {
by := db.GetCode(addr)
if len(by) == 0 {
return nil
}
val := stk.ValidatorWrapper{}
err := rlp.DecodeBytes(by, &val)
if err != nil {
return nil
}
return &val
}
// UpdateStakingInfo update staking information of a given validator (including delegation info)
func (db *DB) UpdateStakingInfo(addr common.Address, val *stk.ValidatorWrapper) error {
by, err := rlp.EncodeToBytes(val)
if err != nil {
return err
}
db.SetCode(addr, by)
return nil
}
// SetValidatorFlag checks whether it is a validator object
func (db *DB) SetValidatorFlag(addr common.Address) {
db.SetState(addr, staking.IsValidatorKey, staking.IsValidator)
}
// UnsetValidatorFlag checks whether it is a validator object
func (db *DB) UnsetValidatorFlag(addr common.Address) {
db.SetState(addr, staking.IsValidatorKey, common.Hash{})
}
// IsValidator checks whether it is a validator object
func (db *DB) IsValidator(addr common.Address) bool {
so := db.getStateObject(addr)
if so == nil {
return false
}
return so.IsValidator(db.db)
}

@ -18,6 +18,7 @@ package core
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
@ -29,6 +30,7 @@ import (
"github.com/harmony-one/harmony/internal/ctxerror"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/internal/utils"
staking "github.com/harmony-one/harmony/staking/types"
)
// StateProcessor is a basic Processor, which takes care of transitioning
@ -179,6 +181,49 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
return receipt, cxReceipt, gas, err
}
// ApplyStakingTransaction attempts to apply a staking transaction to the given state database
// and uses the input parameters for its environment. It returns the receipt
// for the staking transaction, gas used and an error if the transaction failed,
// indicating the block was invalid.
// staking transaction will use the code field in the account to store the staking information
func ApplyStakingTransaction(
config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.DB,
header *block.Header, tx *staking.StakingTransaction, usedGas *uint64, cfg vm.Config) (receipt *types.Receipt, gas uint64, err error) {
msg, err := StakingToMessage(tx, header.Number())
if err != nil {
return nil, 0, err
}
// Create a new context to be used in the EVM environment
context := NewEVMContext(msg, header, bc, author)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(context, statedb, config, cfg)
// Apply the transaction to the current state (included in the env)
gas, err = ApplyStakingMessage(vmenv, msg, gp)
// even there is error, we charge it
if err != nil {
return nil, gas, err
}
// Update the state with pending changes
var root []byte
if config.IsS3(header.Epoch()) {
statedb.Finalise(true)
} else {
root = statedb.IntermediateRoot(config.IsS3(header.Epoch())).Bytes()
}
*usedGas += gas
receipt = types.NewReceipt(root, false, *usedGas)
receipt.TxHash = tx.Hash()
receipt.GasUsed = gas
return receipt, gas, nil
}
// ApplyIncomingReceipt will add amount into ToAddress in the receipt
func ApplyIncomingReceipt(config *params.ChainConfig, db *state.DB, header *block.Header, cxp *types.CXReceiptsProof) error {
if cxp == nil {
@ -199,3 +244,24 @@ func ApplyIncomingReceipt(config *params.ChainConfig, db *state.DB, header *bloc
}
return nil
}
// StakingToMessage returns the staking transaction as a core.Message.
// requires a signer to derive the sender.
// put it here to avoid cyclic import
func StakingToMessage(tx *staking.StakingTransaction, blockNum *big.Int) (types.Message, error) {
payload, err := tx.StakingMsgToBytes()
if err != nil {
return types.Message{}, err
}
from, err := tx.SenderAddress()
if err != nil {
return types.Message{}, err
}
msg := types.NewStakingMessage(from, tx.Nonce(), tx.Gas(), tx.Price(), payload, blockNum)
stkType := tx.StakingType()
if _, ok := types.StakingTypeMap[stkType]; !ok {
return types.Message{}, staking.ErrInvalidStakingKind
}
msg.SetType(types.StakingTypeMap[stkType])
return msg, nil
}

@ -22,14 +22,18 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/core/vm"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/internal/utils"
staking "github.com/harmony-one/harmony/staking/types"
)
var (
errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
errValidatorExist = errors.New("staking validator address already exists")
errValidatorNotExist = errors.New("staking validator address does not exist")
)
/*
@ -75,6 +79,7 @@ type Message interface {
CheckNonce() bool
Data() []byte
Type() types.TransactionType
BlockNum() *big.Int
}
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
@ -134,6 +139,11 @@ func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool,
return NewStateTransition(evm, msg, gp).TransitionDb()
}
// ApplyStakingMessage computes the new state for staking message
func ApplyStakingMessage(evm *vm.EVM, msg Message, gp *GasPool) (uint64, error) {
return NewStateTransition(evm, msg, gp).StakingTransitionDb()
}
// to returns the recipient of the message.
func (st *StateTransition) to() common.Address {
if st.msg == nil || st.msg.To() == nil /* contract creation */ {
@ -252,3 +262,105 @@ func (st *StateTransition) refundGas() {
func (st *StateTransition) gasUsed() uint64 {
return st.initialGas - st.gas
}
// StakingTransitionDb will transition the state by applying the staking message and
// returning the result including the used gas. It returns an error if failed.
// It is used for staking transaction only
func (st *StateTransition) StakingTransitionDb() (usedGas uint64, err error) {
if err = st.preCheck(); err != nil {
return 0, err
}
msg := st.msg
sender := vm.AccountRef(msg.From())
homestead := st.evm.ChainConfig().IsS3(st.evm.EpochNumber) // s3 includes homestead
// Pay intrinsic gas
// TODO: add new formula for staking transaction
gas, err := IntrinsicGas(st.data, false, homestead)
if err != nil {
return 0, err
}
if err = st.useGas(gas); err != nil {
return 0, err
}
// Increment the nonce for the next transaction
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
switch msg.Type() {
case types.StakeNewVal:
stkMsg := &staking.CreateValidator{}
if err = rlp.DecodeBytes(msg.Data(), stkMsg); err != nil {
break
}
err = st.applyCreateValidatorTx(stkMsg, msg.BlockNum())
case types.StakeEditVal:
stkMsg := &staking.EditValidator{}
if err = rlp.DecodeBytes(msg.Data(), stkMsg); err != nil {
break
}
err = st.applyEditValidatorTx(stkMsg, msg.BlockNum())
case types.Delegate:
stkMsg := &staking.Delegate{}
if err = rlp.DecodeBytes(msg.Data(), stkMsg); err != nil {
break
}
err = st.applyDelegateTx(stkMsg)
case types.Undelegate:
stkMsg := &staking.Undelegate{}
if err = rlp.DecodeBytes(msg.Data(), stkMsg); err != nil {
break
}
err = st.applyUndelegateTx(stkMsg)
case types.CollectRewards:
default:
return 0, staking.ErrInvalidStakingKind
}
st.refundGas()
return st.gasUsed(), err
}
func (st *StateTransition) applyCreateValidatorTx(nv *staking.CreateValidator, blockNum *big.Int) error {
if st.state.IsValidator(nv.ValidatorAddress) {
return errValidatorExist
}
v, err := staking.CreateValidatorFromNewMsg(nv)
if err != nil {
return err
}
v.UpdateHeight = blockNum
wrapper := staking.ValidatorWrapper{*v, nil, nil, nil}
if err := st.state.UpdateStakingInfo(v.Address, &wrapper); err != nil {
return err
}
st.state.SetValidatorFlag(v.Address)
return nil
}
func (st *StateTransition) applyEditValidatorTx(ev *staking.EditValidator, blockNum *big.Int) error {
if !st.state.IsValidator(ev.ValidatorAddress) {
return errValidatorNotExist
}
wrapper := st.state.GetStakingInfo(ev.ValidatorAddress)
if err := staking.UpdateValidatorFromEditMsg(&wrapper.Validator, ev); err != nil {
return err
}
wrapper.Validator.UpdateHeight = blockNum
if err := st.state.UpdateStakingInfo(ev.ValidatorAddress, wrapper); err != nil {
return err
}
return nil
}
func (st *StateTransition) applyDelegateTx(delegate *staking.Delegate) error {
return nil
}
func (st *StateTransition) applyUndelegateTx(undelegate *staking.Undelegate) error {
return nil
}

@ -31,6 +31,7 @@ import (
"github.com/harmony-one/harmony/crypto/hash"
common2 "github.com/harmony-one/harmony/internal/common"
staking "github.com/harmony-one/harmony/staking/types"
)
// no go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
@ -48,8 +49,18 @@ const (
SameShardTx TransactionType = iota
SubtractionOnly // only subtract tokens from source shard account
InvalidTx
StakeNewVal
StakeEditVal
Delegate
Undelegate
CollectRewards
)
// StakingTypeMap is the map from staking type to transactionType
var StakingTypeMap = map[staking.Directive]TransactionType{staking.DirectiveCreateValidator: StakeNewVal,
staking.DirectiveEditValidator: StakeEditVal, staking.DirectiveDelegate: Delegate,
staking.DirectiveUndelegate: Undelegate}
// Transaction struct.
type Transaction struct {
data txdata
@ -67,6 +78,16 @@ func (txType TransactionType) String() string {
return "SubtractionOnly"
} else if txType == InvalidTx {
return "InvalidTx"
} else if txType == StakeNewVal {
return "StakeNewValidator"
} else if txType == StakeEditVal {
return "StakeEditValidator"
} else if txType == Delegate {
return "Delegate"
} else if txType == Undelegate {
return "Undelegate"
} else if txType == CollectRewards {
return "CollectRewards"
}
return "Unknown"
}
@ -541,6 +562,7 @@ type Message struct {
gasPrice *big.Int
data []byte
checkNonce bool
blockNum *big.Int
txType TransactionType
}
@ -558,6 +580,20 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
}
}
// NewStakingMessage returns new message of staking type
// always need checkNonce
func NewStakingMessage(from common.Address, nonce uint64, gasLimit uint64, gasPrice *big.Int, data []byte, blockNum *big.Int) Message {
return Message{
from: from,
nonce: nonce,
gasLimit: gasLimit,
gasPrice: new(big.Int).Set(gasPrice),
data: data,
checkNonce: true,
blockNum: blockNum,
}
}
// From returns from address from Message.
func (m Message) From() common.Address {
return m.from
@ -608,6 +644,11 @@ func (m Message) SetType(typ TransactionType) {
m.txType = typ
}
// BlockNum returns the blockNum of the tx belongs to
func (m Message) BlockNum() *big.Int {
return m.blockNum
}
// RecentTxsStats is a recent transactions stats map tracking stats like BlockTxsCounts.
type RecentTxsStats map[uint64]BlockTxsCounts

@ -21,6 +21,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/core/types"
staking "github.com/harmony-one/harmony/staking/types"
)
// StateDB is an EVM database for full state querying.
@ -39,6 +40,12 @@ type StateDB interface {
SetCode(common.Address, []byte)
GetCodeSize(common.Address) int
GetStakingInfo(common.Address) *staking.ValidatorWrapper
UpdateStakingInfo(common.Address, *staking.ValidatorWrapper) error
SetValidatorFlag(common.Address)
UnsetValidatorFlag(common.Address)
IsValidator(common.Address) bool
AddRefund(uint64)
SubRefund(uint64)
GetRefund() uint64

@ -349,15 +349,11 @@ func (node *Node) AddPendingReceipts(receipts *types.CXReceiptsProof) {
// Take out a subset of valid transactions from the pending transaction list
// Note the pending transaction list will then contain the rest of the txs
func (node *Node) getTransactionsForNewBlock(
coinbase common.Address,
) types.Transactions {
func (node *Node) getTransactionsForNewBlock(coinbase common.Address) (types.Transactions, staking.StakingTransactions) {
txsThrottleConfig := core.ShardingSchedule.TxsThrottleConfig()
// the next block number to be added in consensus protocol, which is always one more than current chain header block
newBlockNum := node.Blockchain().CurrentBlock().NumberU64() + 1
// remove old (> txsThrottleConfigRecentTxDuration) blockNum keys from recentTxsStats and initiailize for the new block
for blockNum := range node.recentTxsStats {
recentTxsBlockNumGap := uint64(txsThrottleConfig.RecentTxDuration / node.BlockPeriod)
@ -366,23 +362,20 @@ func (node *Node) getTransactionsForNewBlock(
}
}
node.recentTxsStats[newBlockNum] = make(types.BlockTxsCounts)
// Must update to the correct current state before processing potential txns
if err := node.Worker.UpdateCurrent(coinbase); err != nil {
utils.Logger().Error().
Err(err).
Msg("Failed updating worker's state before txn selection")
return types.Transactions{}
return types.Transactions{}, staking.StakingTransactions{}
}
node.pendingTxMutex.Lock()
defer node.pendingTxMutex.Unlock()
node.pendingStakingTxMutex.Lock()
defer node.pendingStakingTxMutex.Unlock()
pendingTransactions := types.Transactions{}
pendingStakingTransactions := staking.StakingTransactions{}
for _, tx := range node.pendingTransactions {
pendingTransactions = append(pendingTransactions, tx)
}
@ -392,6 +385,9 @@ 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 {
node.pendingTransactions[unselectedTx.Hash()] = unselectedTx
@ -402,7 +398,17 @@ func (node *Node) getTransactionsForNewBlock(
Int("invalidDiscarded", len(invalid)).
Msg("Selecting Transactions")
return selected
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
}
func (node *Node) startRxPipeline(

@ -33,8 +33,8 @@ func TestAddNewBlock(t *testing.T) {
nodeconfig.SetNetworkType(nodeconfig.Devnet)
node := New(host, consensus, testDBFactory, false)
selectedTxs := node.getTransactionsForNewBlock(common.Address{})
node.Worker.CommitTransactions(selectedTxs, common.Address{})
selectedTxs, stks := node.getTransactionsForNewBlock(common.Address{})
node.Worker.CommitTransactions(selectedTxs, stks, common.Address{})
block, _ := node.Worker.FinalizeNewBlock([]byte{}, []byte{}, 0, common.Address{}, nil, nil)
err = node.AddNewBlock(block)
@ -65,8 +65,8 @@ func TestVerifyNewBlock(t *testing.T) {
}
node := New(host, consensus, testDBFactory, false)
selectedTxs := node.getTransactionsForNewBlock(common.Address{})
node.Worker.CommitTransactions(selectedTxs, common.Address{})
selectedTxs, stks := node.getTransactionsForNewBlock(common.Address{})
node.Worker.CommitTransactions(selectedTxs, stks, common.Address{})
block, _ := node.Worker.FinalizeNewBlock([]byte{}, []byte{}, 0, common.Address{}, nil, nil)
if err := node.VerifyNewBlock(block); err != nil {

@ -81,9 +81,9 @@ func (node *Node) proposeNewBlock() (*types.Block, error) {
coinbase := node.Consensus.SelfAddress
// Prepare transactions including staking transactions
selectedTxs := node.getTransactionsForNewBlock(coinbase)
selectedTxs, selectedStakingTxs := node.getTransactionsForNewBlock(coinbase)
if err := node.Worker.CommitTransactions(selectedTxs, coinbase); err != nil {
if err := node.Worker.CommitTransactions(selectedTxs, selectedStakingTxs, coinbase); err != nil {
ctxerror.Log15(utils.GetLogger().Error,
ctxerror.New("cannot commit transactions").
WithCause(err))

@ -13,6 +13,7 @@ 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"
@ -144,6 +145,71 @@ 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")
}
err = w.chain.UpdateValidatorMap(tx)
// keep offchain database consistency with onchain we need revert
// but it should not happend unless local database corrupted
if err != nil {
w.current.state.RevertToSnapshot(snap)
return nil, err
}
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()
@ -152,6 +218,7 @@ func (w *Worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
w.current.header.SetGasUsed(gasUsed)
if err != nil {
w.current.state.RevertToSnapshot(snap)
utils.Logger().Error().Err(err).Str("stakingTxId", tx.Hash().Hex()).Msg("Offchain ValidatorMap Read/Write Error")
return nil, err
}
if receipt == nil {
@ -167,9 +234,8 @@ func (w *Worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
return receipt.Logs, nil
}
// CommitTransactions commits transactions including staking transactions.
func (w *Worker) CommitTransactions(
txs types.Transactions, coinbase common.Address) error {
// CommitTransactions commits transactions.
func (w *Worker) CommitTransactions(txs types.Transactions, stakingTxns staking.StakingTransactions, 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().
@ -190,6 +256,10 @@ func (w *Worker) CommitTransactions(
}
}
for _, stakingTx := range stakingTxns {
_ = stakingTx
// TODO: add logic to commit staking txns
}
return nil
}
@ -331,40 +401,16 @@ 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.receipts, w.current.outcxs, w.current.incxs,
stks,
)
block, err := w.engine.Finalize(w.chain, copyHeader, s, w.current.txs, w.current.receipts, w.current.outcxs, w.current.incxs, w.current.stkingTxs)
if err != nil {
return nil, ctxerror.New("cannot finalize block").WithCause(err)
}
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{

@ -74,7 +74,7 @@ func TestCommitTransactions(t *testing.T) {
tx, _ := types.SignTx(types.NewTransaction(baseNonce, testBankAddress, uint32(0), big.NewInt(int64(denominations.One*randAmount)), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey)
// Commit the tx to the worker
err := worker.CommitTransactions(types.Transactions{tx}, testBankAddress)
err := worker.CommitTransactions(types.Transactions{tx}, nil, testBankAddress)
if err != nil {
t.Error(err)
}

@ -0,0 +1,18 @@
package staking
import (
"github.com/ethereum/go-ethereum/crypto"
)
const (
isValidatorKeyStr = "Harmony/IsValidator/v0"
isValidatorStr = "Harmony/IsAValidator/v0"
isNotValidatorStr = "Harmony/IsNotAValidator/v0"
)
// keys used to retrieve staking related informatio
var (
IsValidatorKey = crypto.Keccak256Hash([]byte(isValidatorKeyStr))
IsValidator = crypto.Keccak256Hash([]byte(isValidatorStr))
IsNotValidator = crypto.Keccak256Hash([]byte(isNotValidatorStr))
)

@ -1,249 +1,72 @@
package types
import (
"fmt"
"errors"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/internal/common"
)
// DVPair is struct that just has a delegator-validator pair with no other data.
// It is intended to be used as a marshalable pointer. For example, a DVPair can be used to construct the
// key to getting an UnbondingDelegation from state.
type DVPair struct {
DelegatorAddress common.Address
ValidatorAddress common.Address
}
// DVVTriplet is struct that just has a delegator-validator-validator triplet with no other data.
// It is intended to be used as a marshalable pointer. For example, a DVVTriplet can be used to construct the
// key to getting a Redelegation from state.
type DVVTriplet struct {
DelegatorAddress common.Address
ValidatorSrcAddress common.Address
ValidatorDstAddress common.Address
}
var (
errInsufficientBalance = errors.New("Insufficient balance to undelegate")
errInvalidAmount = errors.New("Invalid amount, must be positive")
)
// Delegation represents the bond with tokens held by an account. It is
// owned by one delegator, and is associated with the voting power of one
// validator.
type Delegation struct {
DelegatorAddress common.Address `json:"delegator_address" yaml:"delegator_address"`
ValidatorAddress common.Address `json:"validator_address" yaml:"validator_address"`
Amount *big.Int `json:"amount" yaml:"amount"`
Entries []*UndelegationEntry `json:"entries" yaml:"entries"`
}
// UndelegationEntry represents one undelegation entry
type UndelegationEntry struct {
Amount *big.Int
Epoch *big.Int
}
// NewDelegation creates a new delegation object
func NewDelegation(delegatorAddr common.Address, validatorAddr common.Address,
func NewDelegation(delegatorAddr common.Address,
amount *big.Int) Delegation {
return Delegation{
DelegatorAddress: delegatorAddr,
ValidatorAddress: validatorAddr,
Amount: amount,
}
}
// MarshalDelegation return the delegation
func MarshalDelegation(delegation Delegation) ([]byte, error) {
return rlp.EncodeToBytes(delegation)
}
// UnmarshalDelegation return the delegation
func UnmarshalDelegation(by []byte) (*Delegation, error) {
decoded := &Delegation{}
err := rlp.DecodeBytes(by, decoded)
return decoded, err
}
// GetDelegatorAddr returns DelegatorAddr
func (d Delegation) GetDelegatorAddr() common.Address { return d.DelegatorAddress }
// GetValidatorAddr returns ValidatorAddr
func (d Delegation) GetValidatorAddr() common.Address { return d.ValidatorAddress }
// GetAmount returns amount of a delegation
func (d Delegation) GetAmount() *big.Int { return d.Amount }
// String returns a human readable string representation of a Delegation.
func (d Delegation) String() string {
return fmt.Sprintf(`
Delegation:
Delegator: %s
Validator: %s
Amount: %s
`, d.DelegatorAddress, d.ValidatorAddress, d.Amount)
}
// Delegations is a collection of delegations
type Delegations []Delegation
// String returns the string representation of a list of delegations
func (d Delegations) String() (out string) {
for _, del := range d {
out += del.String() + "\n"
}
return strings.TrimSpace(out)
}
// UnbondingDelegation stores all of a single delegator's unbonding bonds
// for a single validator in an time-ordered list
type UnbondingDelegation struct {
DelegatorAddress common.Address `json:"delegator_address" yaml:"delegator_address"` // delegator
ValidatorAddress common.Address `json:"validator_address" yaml:"validator_address"` // validator unbonding from operator addr
Entries []UnbondingDelegationEntry `json:"entries" yaml:"entries"` // unbonding delegation entries
// AddEntry - append entry to the undelegation
func (d *Delegation) AddEntry(epoch *big.Int, amt *big.Int) error {
if d.Amount.Cmp(amt) < 0 {
return errInsufficientBalance
}
// UnbondingDelegationEntry - entry to an UnbondingDelegation
type UnbondingDelegationEntry struct {
ExitEpoch *big.Int `json:"exit_epoch" yaml:"exit_epoch"` // epoch which the unbonding begins
Amount *big.Int `json:"amount" yaml:"amount"` // atoms to receive at completion
if amt.Sign() <= 0 {
return errInvalidAmount
}
d.Amount.Sub(d.Amount, amt)
// NewUnbondingDelegation - create a new unbonding delegation object
func NewUnbondingDelegation(delegatorAddr common.Address,
validatorAddr common.Address, epoch *big.Int, amt *big.Int) UnbondingDelegation {
entry := NewUnbondingDelegationEntry(epoch, amt)
return UnbondingDelegation{
DelegatorAddress: delegatorAddr,
ValidatorAddress: validatorAddr,
Entries: []UnbondingDelegationEntry{entry},
for _, entry := range d.Entries {
if entry.Epoch.Cmp(epoch) == 0 {
entry.Amount.Add(entry.Amount, amt)
return nil
}
}
// NewUnbondingDelegationEntry - create a new unbonding delegation object
func NewUnbondingDelegationEntry(epoch *big.Int, amt *big.Int) UnbondingDelegationEntry {
return UnbondingDelegationEntry{
ExitEpoch: epoch,
Amount: amt,
}
item := UndelegationEntry{amt, epoch}
d.Entries = append(d.Entries, &item)
return nil
}
// AddEntry - append entry to the unbonding delegation
// if there exists same ExitEpoch entry, merge the amount
// TODO: check the total amount not exceed the staking amount call this function
func (d *UnbondingDelegation) AddEntry(epoch *big.Int, amt *big.Int) {
entry := NewUnbondingDelegationEntry(epoch, amt)
for i := range d.Entries {
if d.Entries[i].ExitEpoch == entry.ExitEpoch {
d.Entries[i].Amount.Add(d.Entries[i].Amount, entry.Amount)
return
}
}
// same exit epoch entry not found
d.Entries = append(d.Entries, entry)
return
}
// String returns a human readable string representation of an UnbondingDelegation.
func (d UnbondingDelegation) String() string {
out := fmt.Sprintf(`Unbonding Delegations between:
Delegator: %s
Validator: %s
Entries:`, d.DelegatorAddress, d.ValidatorAddress)
// DeleteEntry - delete an entry from the undelegation
// Opimize it
func (d *Delegation) DeleteEntry(epoch *big.Int) {
entries := []*UndelegationEntry{}
for i, entry := range d.Entries {
out += fmt.Sprintf(` Unbonding Delegation %d:
ExitEpoch: %v
Amount: %s`, i, entry.ExitEpoch, entry.Amount)
if entry.Epoch.Cmp(epoch) == 0 {
entries = append(d.Entries[:i], d.Entries[i+1:]...)
}
return out
}
// UnbondingDelegations is a collection of UnbondingDelegation
type UnbondingDelegations []UnbondingDelegation
func (ubds UnbondingDelegations) String() (out string) {
for _, u := range ubds {
out += u.String() + "\n"
}
return strings.TrimSpace(out)
}
// Redelegation contains the list of a particular delegator's
// redelegating bonds from a particular source validator to a
// particular destination validator
type Redelegation struct {
DelegatorAddress common.Address `json:"delegator_address" yaml:"delegator_address"` // delegator
ValidatorSrcAddress common.Address `json:"validator_src_address" yaml:"validator_src_address"` // validator redelegation source operator addr
ValidatorDstAddress common.Address `json:"validator_dst_address" yaml:"validator_dst_address"` // validator redelegation destination operator addr
Entries []RedelegationEntry `json:"entries" yaml:"entries"` // redelegation entries
}
// RedelegationEntry - entry to a Redelegation
type RedelegationEntry struct {
Epoch *big.Int `json:"epoch" yaml:"epoch"` // epoch at which the redelegation took place
Amount *big.Int `json:"amount" yaml:"amount"` // amount of destination-validator tokens created by redelegation
}
// NewRedelegation - create a new redelegation object
func NewRedelegation(delegatorAddr common.Address, validatorSrcAddr,
validatorDstAddr common.Address, epoch *big.Int, amt *big.Int) Redelegation {
entry := NewRedelegationEntry(epoch, amt)
return Redelegation{
DelegatorAddress: delegatorAddr,
ValidatorSrcAddress: validatorSrcAddr,
ValidatorDstAddress: validatorDstAddr,
Entries: []RedelegationEntry{entry},
}
}
// NewRedelegationEntry - create a new redelegation object
func NewRedelegationEntry(epoch *big.Int, amt *big.Int) RedelegationEntry {
return RedelegationEntry{
Epoch: epoch,
Amount: amt,
}
}
// AddEntry - append entry to the unbonding delegation
// Merge if has same epoch field
func (d *Redelegation) AddEntry(epoch *big.Int, amt *big.Int) {
entry := NewRedelegationEntry(epoch, amt)
for i := range d.Entries {
if d.Entries[i].Epoch == entry.Epoch {
d.Entries[i].Amount.Add(d.Entries[i].Amount, entry.Amount)
return
}
}
// same epoch entry not found
d.Entries = append(d.Entries, entry)
return
}
// String returns a human readable string representation of a Redelegation.
func (d Redelegation) String() string {
out := fmt.Sprintf(`
Redelegations between:
Delegator: %s
Source Validator: %s
Destination Validator: %s
Entries:
`,
d.DelegatorAddress, d.ValidatorSrcAddress, d.ValidatorDstAddress,
)
for i, entry := range d.Entries {
out += fmt.Sprintf(` Redelegation Entry #%d:
Epoch: %v
Amount: %v
`,
i, entry.Epoch, entry.Amount,
)
}
return strings.TrimRight(out, "\n")
}
// Redelegations are a collection of Redelegation
type Redelegations []Redelegation
func (d Redelegations) String() (out string) {
for _, red := range d {
out += red.String() + "\n"
if entries != nil {
d.Entries = entries
}
return strings.TrimSpace(out)
}

@ -4,11 +4,10 @@ import (
"fmt"
"math/big"
"github.com/harmony-one/harmony/shard"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
"github.com/pkg/errors"
)
@ -52,7 +51,7 @@ func (d Directive) String() string {
// CreateValidator - type for creating a new validator
type CreateValidator struct {
Description `json:"description" yaml:"description"`
Description *Description `json:"description" yaml:"description"`
CommissionRates `json:"commission" yaml:"commission"`
MinSelfDelegation *big.Int `json:"min_self_delegation" yaml:"min_self_delegation"`
MaxTotalDelegation *big.Int `json:"max_total_delegation" yaml:"max_total_delegation"`

@ -1,14 +1,15 @@
package types
import (
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/internal/ctxerror"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
)
// Define validator staking related const
@ -20,10 +21,16 @@ const (
MaxDetailsLength = 280
)
var (
errAddressNotMatch = errors.New("Validator key not match")
)
// ValidatorWrapper contains validator and its delegation information
type ValidatorWrapper struct {
Validator `json:"validator" yaml:"validator"`
Delegations []Delegation `json:"delegations" yaml:"delegations"`
SnapshotValidator *Validator `json:"snapshot_validator" yaml:"snaphost_validator"`
SnapshotDelegations []Delegation `json:"snapshot_delegations" yaml:"snapshot_delegations"`
}
// Validator - data fields for a validator
@ -31,7 +38,7 @@ type Validator struct {
// ECDSA address of the validator
Address common.Address `json:"address" yaml:"address"`
// The BLS public key of the validator for consensus
ValidatingPubKey bls.PublicKey `json:"validating_pub_key" yaml:"validating_pub_key"`
SlotPubKeys []shard.BlsPublicKey `json:"validating_pub_key" yaml:"validating_pub_key"`
// The stake put by the validator itself
Stake *big.Int `json:"stake" yaml:"stake"`
// if unbonding, height at which this validator has begun unbonding
@ -80,7 +87,7 @@ func NewDescription(name, identity, website, securityContact, details string) De
// UpdateDescription updates the fields of a given description. An error is
// returned if the resulting description contains an invalid length.
func (d Description) UpdateDescription(d2 Description) (Description, error) {
func UpdateDescription(d2 *Description) (Description, error) {
return NewDescription(
d2.Name,
d2.Identity,
@ -125,3 +132,40 @@ func (v *Validator) GetCommissionRate() numeric.Dec { return v.Commission.Rate }
// GetMinSelfDelegation returns the minimum amount the validator must stake
func (v *Validator) GetMinSelfDelegation() *big.Int { return v.MinSelfDelegation }
// CreateValidatorFromNewMsg creates validator from NewValidator message
func CreateValidatorFromNewMsg(val *CreateValidator) (*Validator, error) {
desc, err := UpdateDescription(val.Description)
if err != nil {
return nil, err
}
commission := Commission{val.CommissionRates, new(big.Int)}
pubKeys := []shard.BlsPublicKey{}
pubKeys = append(pubKeys, val.SlotPubKeys...)
v := Validator{val.ValidatorAddress, pubKeys,
val.Amount, new(big.Int), val.MinSelfDelegation, false,
commission, desc}
return &v, nil
}
// UpdateValidatorFromEditMsg updates validator from EditValidator message
func UpdateValidatorFromEditMsg(validator *Validator, edit *EditValidator) error {
if validator.Address != edit.ValidatorAddress {
return errAddressNotMatch
}
desc, err := UpdateDescription(edit.Description)
if err != nil {
return err
}
validator.Description = desc
if edit.CommissionRate != nil {
validator.Rate = *edit.CommissionRate
}
if edit.MinSelfDelegation != nil {
validator.MinSelfDelegation = edit.MinSelfDelegation
}
return nil
}

@ -4,8 +4,6 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/numeric"
)
@ -13,7 +11,7 @@ func CreateNewValidator() Validator {
cr := CommissionRates{Rate: numeric.OneDec(), MaxRate: numeric.OneDec(), MaxChangeRate: numeric.ZeroDec()}
c := Commission{cr, big.NewInt(300)}
d := Description{Name: "SuperHero", Identity: "YouWillNotKnow", Website: "under_construction", Details: "N/A"}
v := Validator{Address: common.Address{}, ValidatingPubKey: *bls.RandPrivateKey().GetPublicKey(),
v := Validator{Address: common.Address{}, SlotPubKeys: nil,
Stake: big.NewInt(500), UnbondingHeight: big.NewInt(20), MinSelfDelegation: big.NewInt(7),
Active: false, Commission: c, Description: d}
return v

@ -125,7 +125,7 @@ func fundFaucetContract(chain *core.BlockChain) {
amount := 720000
tx, _ := types.SignTx(types.NewTransaction(nonce+uint64(4), StakingAddress, 0, big.NewInt(int64(amount)), params.TxGas, nil, nil), types.HomesteadSigner{}, FaucetPriKey)
txs = append(txs, tx)
err := contractworker.CommitTransactions(txs, testUserAddress)
err := contractworker.CommitTransactions(txs, nil, testUserAddress)
if err != nil {
fmt.Println(err)
}
@ -163,7 +163,7 @@ func callFaucetContractToFundAnAddress(chain *core.BlockChain) {
callEnc = append(callEnc, paddedAddress...)
callfaucettx, _ := types.SignTx(types.NewTransaction(nonce+uint64(5), faucetContractAddress, 0, big.NewInt(0), params.TxGasContractCreation*10, nil, callEnc), types.HomesteadSigner{}, FaucetPriKey)
err = contractworker.CommitTransactions(types.Transactions{callfaucettx}, testUserAddress)
err = contractworker.CommitTransactions(types.Transactions{callfaucettx}, nil, testUserAddress)
if err != nil {
fmt.Println(err)
}
@ -241,7 +241,7 @@ func playStaking(chain *core.BlockChain) {
tx, _ := types.SignTx(types.NewTransaction(0, stakeContractAddress, 0, big.NewInt(int64(stake)), params.TxGas*5, nil, callEncl), types.HomesteadSigner{}, allRandomUserKey[i])
stakingtxns = append(stakingtxns, tx)
}
err = contractworker.CommitTransactions(stakingtxns, common.Address{})
err = contractworker.CommitTransactions(stakingtxns, nil, common.Address{})
if err != nil {
fmt.Println(err)
@ -299,7 +299,7 @@ func playWithdrawStaking(chain *core.BlockChain) {
withdrawstakingtxns = append(withdrawstakingtxns, tx)
}
err = contractworker.CommitTransactions(withdrawstakingtxns, common.Address{})
err = contractworker.CommitTransactions(withdrawstakingtxns, nil, common.Address{})
if err != nil {
fmt.Println("error:")
fmt.Println(err)

Loading…
Cancel
Save