github.com/cheng762/platon-go@v1.8.17-0.20190529111256-7deff2d7be26/core/vm/candidate_pool.go (about)

     1  package vm
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"github.com/PlatONnetwork/PlatON-Go/common"
     8  	"github.com/PlatONnetwork/PlatON-Go/core/types"
     9  	"github.com/PlatONnetwork/PlatON-Go/crypto"
    10  	"github.com/PlatONnetwork/PlatON-Go/log"
    11  	"github.com/PlatONnetwork/PlatON-Go/p2p/discover"
    12  	"github.com/PlatONnetwork/PlatON-Go/params"
    13  	"github.com/PlatONnetwork/PlatON-Go/rlp"
    14  	"math/big"
    15  )
    16  
    17  var (
    18  	ErrOwnerNotOnly          = errors.New("Node ID cannot bind multiple owners")
    19  	ErrPermissionDenied      = errors.New("Transaction from address permission denied")
    20  	ErrFeeIllegal            = errors.New("The fee is illegal")
    21  	ErrDepositEmpty          = errors.New("Deposit balance not zero")
    22  	ErrWithdrawEmpty         = errors.New("No withdrawal amount")
    23  	ErrCandidatePoolEmpty    = errors.New("Candidate Pool is null")
    24  	ErrCandidateNotExist     = errors.New("The candidate is not exist")
    25  	ErrCandidateAlreadyExist = errors.New("The candidate is already exist")
    26  )
    27  
    28  const (
    29  	CandidateDepositEvent       = "CandidateDepositEvent"
    30  	CandidateApplyWithdrawEvent = "CandidateApplyWithdrawEvent"
    31  	CandidateWithdrawEvent      = "CandidateWithdrawEvent"
    32  	SetCandidateExtraEvent      = "SetCandidateExtraEvent"
    33  )
    34  
    35  type candidatePoolContext interface {
    36  	SetCandidate(state StateDB, nodeId discover.NodeID, can *types.Candidate) error
    37  	GetCandidate(state StateDB, nodeId discover.NodeID, blockNumber *big.Int) *types.Candidate
    38  	GetCandidateArr(state StateDB, blockNumber *big.Int, nodeIds ...discover.NodeID) types.CandidateQueue
    39  	WithdrawCandidate(state StateDB, nodeId discover.NodeID, price, blockNumber *big.Int) error
    40  	GetChosens(state StateDB, flag int, blockNumber *big.Int) types.KindCanQueue
    41  	GetChairpersons(state StateDB, blockNumber *big.Int) types.CandidateQueue
    42  	GetDefeat(state StateDB, nodeId discover.NodeID, blockNumber *big.Int) types.RefundQueue
    43  	IsDefeat(state StateDB, nodeId discover.NodeID, blockNumber *big.Int) bool
    44  	IsChosens(state StateDB, nodeId discover.NodeID, blockNumber *big.Int) bool
    45  	RefundBalance(state StateDB, nodeId discover.NodeID, blockNumber *big.Int) error
    46  	GetOwner(state StateDB, nodeId discover.NodeID, blockNumber *big.Int) common.Address
    47  	SetCandidateExtra(state StateDB, nodeId discover.NodeID, extra string) error
    48  	GetRefundInterval(blockNumber *big.Int) uint32
    49  	MaxCount() uint32
    50  	MaxChair() uint32
    51  }
    52  
    53  type CandidateContract struct {
    54  	Contract *Contract
    55  	Evm      *EVM
    56  }
    57  
    58  func (c *CandidateContract) RequiredGas(input []byte) uint64 {
    59  	return params.EcrecoverGas
    60  }
    61  
    62  func (c *CandidateContract) Run(input []byte) ([]byte, error) {
    63  	if nil == c.Evm.CandidatePoolContext {
    64  		log.Error("Failed to CandidateContract Run", "ErrCandidatePoolEmpty: ", ErrCandidatePoolEmpty.Error())
    65  		return nil, ErrCandidatePoolEmpty
    66  	}
    67  	var command = map[string]interface{}{
    68  		"CandidateDeposit":          c.CandidateDeposit,
    69  		"CandidateApplyWithdraw":    c.CandidateApplyWithdraw,
    70  		"CandidateWithdraw":         c.CandidateWithdraw,
    71  		"SetCandidateExtra":         c.SetCandidateExtra,
    72  		"GetCandidateWithdrawInfos": c.GetCandidateWithdrawInfos,
    73  		"GetCandidateDetails":       c.GetCandidateDetails,
    74  		"GetCandidateList":          c.GetCandidateList,
    75  		"GetVerifiersList":          c.GetVerifiersList,
    76  	}
    77  	return execute(input, command)
    78  }
    79  
    80  // Candidate Application && Increase Quality Deposit
    81  func (c *CandidateContract) CandidateDeposit(nodeId discover.NodeID, owner common.Address, fee uint32, host, port, extra string) ([]byte, error) {
    82  	deposit := c.Contract.value
    83  	txHash := c.Evm.StateDB.TxHash()
    84  	txIdx := c.Evm.StateDB.TxIdx()
    85  	height := c.Evm.Context.BlockNumber
    86  	//from := c.Contract.caller.Address()
    87  	log.Info("Input to CandidateDeposit", "blockNumber", height.String(), "nodeId: ", nodeId.String(), " owner: ", owner.Hex(), " deposit: ", deposit,
    88  		"  fee: ", fee, " txhash: ", txHash.Hex(), " txIdx: ", txIdx, " height: ", height, " host: ", host, " port: ", port, " extra: ", extra)
    89  	if fee > 10000 {
    90  		log.Error("Failed to CandidateDeposit", "blockNumber", height.String(), "ErrFeeIllegal: ", ErrFeeIllegal.Error())
    91  		return nil, ErrFeeIllegal
    92  	}
    93  	if deposit.Cmp(big.NewInt(0)) < 1 {
    94  		log.Error("Failed to CandidateDeposit", "blockNumber", height.String(), "ErrDepositEmpty: ", ErrDepositEmpty.Error())
    95  		return nil, ErrDepositEmpty
    96  	}
    97  	addr := c.Evm.CandidatePoolContext.GetOwner(c.Evm.StateDB, nodeId, height)
    98  	if common.ZeroAddr != addr {
    99  		if ok := bytes.Equal(addr.Bytes(), owner.Bytes()); !ok {
   100  			log.Error("Failed to CandidateDeposit==> ", "blockNumber", height.String(), "old owner", addr.Hex(), "new owner", owner, "ErrOwnerNotOnly: ", ErrOwnerNotOnly.Error())
   101  			return nil, ErrOwnerNotOnly
   102  		}
   103  	}
   104  	//var alldeposit *big.Int
   105  	var txhash common.Hash
   106  	var towner common.Address
   107  	can := c.Evm.CandidatePoolContext.GetCandidate(c.Evm.StateDB, nodeId, height)
   108  	if nil != can {
   109  		log.Error("Failed to CandidateDeposit, the candidate is already exist", "blockNumber", height.String(), "nodeId", nodeId.String())
   110  		return nil, ErrCandidateAlreadyExist
   111  	}
   112  	//if nil != can {
   113  	//	alldeposit = new(big.Int).Add(can.Deposit, deposit)
   114  	//	txhash = can.TxHash
   115  	//	towner = can.TOwner
   116  	//	log.Info("CandidateDeposit==> ", "alldeposit: ", alldeposit, " can.Deposit: ", can.Deposit, " deposit: ", deposit)
   117  	//} else {
   118  	//	alldeposit = deposit
   119  	//}
   120  	canDeposit := types.Candidate{
   121  		//alldeposit,
   122  		deposit,
   123  		height,
   124  		txIdx,
   125  		nodeId,
   126  		host,
   127  		port,
   128  		owner,
   129  		extra,
   130  		fee,
   131  		txhash,
   132  		towner,
   133  	}
   134  	log.Info("CandidateDeposit", "blockNumber", height.String(), "canDeposit: ", canDeposit)
   135  	if err := c.Evm.CandidatePoolContext.SetCandidate(c.Evm.StateDB, nodeId, &canDeposit); nil != err {
   136  		log.Error("Failed to CandidateDeposit", "blockNumber", height.String(), "SetCandidate return err: ", err.Error())
   137  		return nil, err
   138  	}
   139  	r := ResultCommon{true, "", "success"}
   140  	event, _ := json.Marshal(r)
   141  	c.addLog(CandidateDepositEvent, string(event))
   142  	log.Info("Result of CandidateDeposit", "blockNumber", height.String(), "json: ", string(event))
   143  	return nil, nil
   144  }
   145  
   146  // Apply for a refund of the deposit
   147  func (c *CandidateContract) CandidateApplyWithdraw(nodeId discover.NodeID, withdraw *big.Int) ([]byte, error) {
   148  	txHash := c.Evm.StateDB.TxHash()
   149  	from := c.Contract.caller.Address()
   150  	height := c.Evm.Context.BlockNumber
   151  	log.Info("Input to CandidateApplyWithdraw on WithdrawCandidate", "blockNumber", height.String(), "nodeId: ", nodeId.String(), " from: ", from.Hex(), " txHash: ", txHash.Hex(), " withdraw: ", withdraw, " height: ", height)
   152  	can := c.Evm.CandidatePoolContext.GetCandidate(c.Evm.StateDB, nodeId, height)
   153  
   154  	if nil == can {
   155  		log.Error("Failed to CandidateApplyWithdraw on WithdrawCandidate", "blockNumber", height.String(), "ErrCandidateNotExist: ", ErrCandidateNotExist.Error())
   156  		return nil, ErrCandidateNotExist
   157  	}
   158  	if can.Deposit.Cmp(big.NewInt(0)) < 1 {
   159  		log.Error("Failed to CandidateApplyWithdraw on WithdrawCandidate", "blockNumber", height.String(), "ErrWithdrawEmpty: ", ErrWithdrawEmpty.Error())
   160  		return nil, ErrWithdrawEmpty
   161  	}
   162  	if ok := bytes.Equal(can.Owner.Bytes(), from.Bytes()); !ok {
   163  		log.Error("Failed to CandidateApplyWithdraw on WithdrawCandidate", "blockNumber", height.String(), "ErrPermissionDenied: ", ErrPermissionDenied.Error())
   164  		return nil, ErrPermissionDenied
   165  	}
   166  	if withdraw.Cmp(can.Deposit) > 0 {
   167  		withdraw = can.Deposit
   168  	}
   169  	if err := c.Evm.CandidatePoolContext.WithdrawCandidate(c.Evm.StateDB, nodeId, withdraw, height); nil != err {
   170  		log.Error("Failed to CandidateApplyWithdraw on WithdrawCandidate", "blockNumber", height.String(), "WithdrawCandidate return err: ", err.Error())
   171  		return nil, err
   172  	}
   173  	r := ResultCommon{true, "", "success"}
   174  	event, _ := json.Marshal(r)
   175  	c.addLog(CandidateApplyWithdrawEvent, string(event))
   176  	log.Info("Result of CandidateApplyWithdraw on WithdrawCandidate", "blockNumber", height.String(), "json: ", string(event))
   177  	return nil, nil
   178  }
   179  
   180  // Deposit withdrawal
   181  func (c *CandidateContract) CandidateWithdraw(nodeId discover.NodeID) ([]byte, error) {
   182  	txHash := c.Evm.StateDB.TxHash()
   183  	height := c.Evm.Context.BlockNumber
   184  	log.Info("Input to CandidateWithdraw to RefundBalance", "nodeId: ", nodeId.String(), " height: ", height, " txHash: ", txHash.Hex())
   185  	if err := c.Evm.CandidatePoolContext.RefundBalance(c.Evm.StateDB, nodeId, height); nil != err {
   186  		log.Error("Failed to CandidateWithdraw to RefundBalance", "blockNumber", height.String(), "RefundBalance return err: ", err.Error())
   187  		return nil, err
   188  	}
   189  	r := ResultCommon{true, "", "success"}
   190  	event, _ := json.Marshal(r)
   191  	c.addLog(CandidateWithdrawEvent, string(event))
   192  	log.Info("Result of CandidateWithdraw to RefundBalance", "blockNumber", height.String(), "json: ", string(event))
   193  	return nil, nil
   194  }
   195  
   196  // Set up additional information
   197  func (c *CandidateContract) SetCandidateExtra(nodeId discover.NodeID, extra string) ([]byte, error) {
   198  	txHash := c.Evm.StateDB.TxHash()
   199  	from := c.Contract.caller.Address()
   200  	height := c.Evm.Context.BlockNumber
   201  	log.Info("Input to SetCandidateExtra", "blockNumber", height.String(), "nodeId: ", nodeId.String(), " extra: ", extra, " from: ", from.Hex(), " txHash: ", txHash.Hex())
   202  	owner := c.Evm.CandidatePoolContext.GetOwner(c.Evm.StateDB, nodeId, height)
   203  	if ok := bytes.Equal(owner.Bytes(), from.Bytes()); !ok {
   204  		log.Error("Failed to SetCandidateExtra", "blockNumber", height.String(), "ErrPermissionDenied: ", ErrPermissionDenied.Error())
   205  		return nil, ErrPermissionDenied
   206  	}
   207  	if err := c.Evm.CandidatePoolContext.SetCandidateExtra(c.Evm.StateDB, nodeId, extra); nil != err {
   208  		log.Error("Failed to SetCandidateExtra", "blockNumber", height.String(), "SetCandidateExtra return err: ", err.Error())
   209  		return nil, err
   210  	}
   211  	r := ResultCommon{true, "", "success"}
   212  	event, _ := json.Marshal(r)
   213  	c.addLog(SetCandidateExtraEvent, string(event))
   214  	log.Info("Result of SetCandidateExtra", "blockNumber", height.String(), "json: ", string(event))
   215  	return nil, nil
   216  }
   217  
   218  // Get the refund history you have applied for
   219  func (c *CandidateContract) GetCandidateWithdrawInfos(nodeId discover.NodeID) ([]byte, error) {
   220  	height := c.Evm.Context.BlockNumber
   221  	log.Info("Input to CandidateWithdrawInfos", "blockNumber", height.String(), "nodeId: ", nodeId.String())
   222  
   223  	refunds := c.Evm.CandidatePoolContext.GetDefeat(c.Evm.StateDB, nodeId, height)
   224  	type WithdrawInfo struct {
   225  		Balance        *big.Int
   226  		LockNumber     *big.Int
   227  		LockBlockCycle uint32
   228  	}
   229  	r := make([]WithdrawInfo, len(refunds))
   230  	for i, v := range refunds {
   231  		refundBlockNumber := c.Evm.CandidatePoolContext.GetRefundInterval(height)
   232  		log.Debug("Call CandidateWithdrawInfos", "Deposit", v.Deposit, "BlockNumber", v.BlockNumber.String(), "RefundBlockNumber", refundBlockNumber)
   233  		r[i] = WithdrawInfo{v.Deposit, v.BlockNumber, refundBlockNumber}
   234  	}
   235  	data, _ := json.Marshal(r)
   236  	sdata := DecodeResultStr(string(data))
   237  	log.Info("Result of CandidateWithdrawInfos", "blockNumber", height.String(), "json: ", string(data))
   238  	return sdata, nil
   239  }
   240  
   241  // GetCandidateDetails returns the batch of candidate info.
   242  func (c *CandidateContract) GetCandidateDetails(nodeIds []discover.NodeID) ([]byte, error) {
   243  
   244  	height := c.Evm.Context.BlockNumber
   245  	input, _ := json.Marshal(nodeIds)
   246  	log.Info("Input to GetCandidateDetails", "blockNumber", height.String(), "length: ", len(nodeIds), " nodeIds: ", string(input))
   247  	candidates := c.Evm.CandidatePoolContext.GetCandidateArr(c.Evm.StateDB, height, nodeIds...)
   248  	data, _ := json.Marshal(candidates)
   249  	sdata := DecodeResultStr(string(data))
   250  	log.Info("Result of GetCandidateDetails", "blockNumber", height.String(), "len(candidates): ", len(candidates), "json: ", string(data))
   251  	return sdata, nil
   252  }
   253  
   254  // Get the current block candidate list
   255  func (c *CandidateContract) GetCandidateList() ([]byte, error) {
   256  
   257  	height := c.Evm.Context.BlockNumber
   258  	candidates := c.Evm.CandidatePoolContext.GetChosens(c.Evm.StateDB, 0, height)
   259  	data, _ := json.Marshal(candidates)
   260  	sdata := DecodeResultStr(string(data))
   261  	log.Info("Result of GetCandidateList", "blockNumber", height.String(), "len(candidates): ", len(candidates[0])+len(candidates[1]), "json: ", string(data))
   262  	return sdata, nil
   263  }
   264  
   265  // Get the current block round certifier list
   266  func (c *CandidateContract) GetVerifiersList() ([]byte, error) {
   267  	height := c.Evm.Context.BlockNumber
   268  	verifiers := c.Evm.CandidatePoolContext.GetChairpersons(c.Evm.StateDB, height)
   269  	data, _ := json.Marshal(verifiers)
   270  	sdata := DecodeResultStr(string(data))
   271  	log.Info("Result of GetVerifiersList", "blockNumber", height.String(), "len(verifiers): ", len(verifiers), "json: ", string(data))
   272  	return sdata, nil
   273  }
   274  
   275  // addLog let the result add to event.
   276  func (c *CandidateContract) addLog(event, data string) {
   277  	var logdata [][]byte
   278  	logdata = make([][]byte, 0)
   279  	logdata = append(logdata, []byte(data))
   280  	buf := new(bytes.Buffer)
   281  	if err := rlp.Encode(buf, logdata); nil != err {
   282  		log.Error("Failed to CandidateContract addlog", "rlp encode fail: ", err.Error())
   283  	}
   284  	c.Evm.StateDB.AddLog(&types.Log{
   285  		Address:     common.CandidatePoolAddr,
   286  		Topics:      []common.Hash{common.BytesToHash(crypto.Keccak256([]byte(event)))},
   287  		Data:        buf.Bytes(),
   288  		BlockNumber: c.Evm.Context.BlockNumber.Uint64(),
   289  	})
   290  }