github.com/aswedchain/aswed@v1.0.1/consensus/congress/congress_govern.go (about)

     1  package congress
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"github.com/aswedchain/aswed/accounts"
     8  	"github.com/aswedchain/aswed/common"
     9  	"github.com/aswedchain/aswed/common/hexutil"
    10  	"github.com/aswedchain/aswed/consensus"
    11  	"github.com/aswedchain/aswed/consensus/congress/systemcontract"
    12  	"github.com/aswedchain/aswed/consensus/congress/vmcaller"
    13  	"github.com/aswedchain/aswed/core/state"
    14  	"github.com/aswedchain/aswed/core/types"
    15  	"github.com/aswedchain/aswed/log"
    16  	"github.com/aswedchain/aswed/rlp"
    17  	"math"
    18  	"math/big"
    19  )
    20  
    21  // Proposal is the system governance proposal info.
    22  type Proposal struct {
    23  	Id     *big.Int
    24  	Action *big.Int
    25  	From   common.Address
    26  	To     common.Address
    27  	Value  *big.Int
    28  	Data   []byte
    29  }
    30  
    31  func (c *Congress) getPassedProposalCount(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB) (uint32, error) {
    32  
    33  	method := "getPassedProposalCount"
    34  	data, err := c.abi[systemcontract.SysGovContractName].Pack(method)
    35  	if err != nil {
    36  		log.Error("Can't pack data for getPassedProposalCount", "error", err)
    37  		return 0, err
    38  	}
    39  
    40  	msg := types.NewMessage(header.Coinbase, &systemcontract.SysGovContractAddr, 0, new(big.Int), math.MaxUint64, new(big.Int), data, false)
    41  
    42  	// use parent
    43  	result, err := vmcaller.ExecuteMsg(msg, state, header, newChainContext(chain, c), c.chainConfig)
    44  	if err != nil {
    45  		return 0, err
    46  	}
    47  
    48  	// unpack data
    49  	ret, err := c.abi[systemcontract.SysGovContractName].Unpack(method, result)
    50  	if err != nil {
    51  		return 0, err
    52  	}
    53  	if len(ret) != 1 {
    54  		return 0, errors.New("invalid output length")
    55  	}
    56  	count, ok := ret[0].(uint32)
    57  	if !ok {
    58  		return 0, errors.New("invalid count format")
    59  	}
    60  
    61  	return count, nil
    62  }
    63  
    64  func (c *Congress) getPassedProposalByIndex(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, idx uint32) (*Proposal, error) {
    65  
    66  	method := "getPassedProposalByIndex"
    67  	data, err := c.abi[systemcontract.SysGovContractName].Pack(method, idx)
    68  	if err != nil {
    69  		log.Error("Can't pack data for getPassedProposalByIndex", "error", err)
    70  		return nil, err
    71  	}
    72  
    73  	msg := types.NewMessage(header.Coinbase, &systemcontract.SysGovContractAddr, 0, new(big.Int), math.MaxUint64, new(big.Int), data, false)
    74  
    75  	// use parent
    76  	result, err := vmcaller.ExecuteMsg(msg, state, header, newChainContext(chain, c), c.chainConfig)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	// unpack data
    82  	prop := &Proposal{}
    83  	err = c.abi[systemcontract.SysGovContractName].UnpackIntoInterface(prop, method, result)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	return prop, nil
    89  }
    90  
    91  //finishProposalById
    92  func (c *Congress) finishProposalById(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, id *big.Int) error {
    93  	method := "finishProposalById"
    94  	data, err := c.abi[systemcontract.SysGovContractName].Pack(method, id)
    95  	if err != nil {
    96  		log.Error("Can't pack data for getPassedProposalByIndex", "error", err)
    97  		return err
    98  	}
    99  
   100  	msg := types.NewMessage(header.Coinbase, &systemcontract.SysGovContractAddr, 0, new(big.Int), math.MaxUint64, new(big.Int), data, false)
   101  
   102  	// execute message without a transaction
   103  	state.Prepare(common.Hash{}, header.Hash(), 0)
   104  	_, err = vmcaller.ExecuteMsg(msg, state, header, newChainContext(chain, c), c.chainConfig)
   105  	if err != nil {
   106  		return err
   107  	}
   108  
   109  	return nil
   110  }
   111  
   112  func (c *Congress) executeProposal(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, prop *Proposal, totalTxIndex int) (*types.Transaction, *types.Receipt, error) {
   113  	// Even if the miner is not `running`, it's still working,
   114  	// the 'miner.worker' will try to FinalizeAndAssemble a block,
   115  	// in this case, the signTxFn is not set. A `non-miner node` can't execute system governance proposal.
   116  	if c.signTxFn == nil {
   117  		return nil, nil, errors.New("signTxFn not set")
   118  	}
   119  
   120  	propRLP, err := rlp.EncodeToBytes(prop)
   121  	if err != nil {
   122  		return nil, nil, err
   123  	}
   124  	//make system governance transaction
   125  	nonce := state.GetNonce(c.validator)
   126  	tx := types.NewTransaction(nonce, systemcontract.SysGovToAddr, prop.Value, header.GasLimit, new(big.Int), propRLP)
   127  	tx, err = c.signTxFn(accounts.Account{Address: c.validator}, tx, chain.Config().ChainID)
   128  	if err != nil {
   129  		return nil, nil, err
   130  	}
   131  	//add nonce for validator
   132  	state.SetNonce(c.validator, nonce+1)
   133  	receipt := c.executeProposalMsg(chain, header, state, prop, totalTxIndex, tx.Hash(), common.Hash{})
   134  
   135  	return tx, receipt, nil
   136  }
   137  
   138  func (c *Congress) replayProposal(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, prop *Proposal, totalTxIndex int, tx *types.Transaction) (*types.Receipt, error) {
   139  	sender, err := types.Sender(c.signer, tx)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	if sender != header.Coinbase {
   144  		return nil, errors.New("invalid sender for system governance transaction")
   145  	}
   146  	propRLP, err := rlp.EncodeToBytes(prop)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	if !bytes.Equal(propRLP, tx.Data()) {
   151  		return nil, fmt.Errorf("data missmatch, proposalID: %s, rlp: %s, txHash:%s, txData:%s", prop.Id.String(), hexutil.Encode(propRLP), tx.Hash().String(), hexutil.Encode(tx.Data()))
   152  	}
   153  	//make system governance transaction
   154  	nonce := state.GetNonce(sender)
   155  	//add nonce for validator
   156  	state.SetNonce(sender, nonce+1)
   157  	receipt := c.executeProposalMsg(chain, header, state, prop, totalTxIndex, tx.Hash(), header.Hash())
   158  
   159  	return receipt, nil
   160  }
   161  
   162  func (c *Congress) executeProposalMsg(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, prop *Proposal, totalTxIndex int, txHash, bHash common.Hash) *types.Receipt {
   163  	var receipt *types.Receipt
   164  	action := prop.Action.Uint64()
   165  	switch action {
   166  	case 0:
   167  		// evm action.
   168  		receipt = c.executeEvmCallProposal(chain, header, state, prop, totalTxIndex, txHash, bHash)
   169  	case 1:
   170  		// delete code action
   171  		ok := state.Erase(prop.To)
   172  		receipt = types.NewReceipt([]byte{}, ok != true, header.GasUsed)
   173  		log.Info("executeProposalMsg", "action", "erase", "id", prop.Id.String(), "to", prop.To, "txHash", txHash.String(), "success", ok)
   174  	default:
   175  		receipt = types.NewReceipt([]byte{}, true, header.GasUsed)
   176  		log.Warn("executeProposalMsg failed, unsupported action", "action", action, "id", prop.Id.String(), "from", prop.From, "to", prop.To, "value", prop.Value.String(), "data", hexutil.Encode(prop.Data), "txHash", txHash.String())
   177  	}
   178  
   179  	receipt.TxHash = txHash
   180  	receipt.BlockHash = state.BlockHash()
   181  	receipt.BlockNumber = header.Number
   182  	receipt.TransactionIndex = uint(state.TxIndex())
   183  
   184  	return receipt
   185  }
   186  
   187  // the returned value should not nil.
   188  func (c *Congress) executeEvmCallProposal(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, prop *Proposal, totalTxIndex int, txHash, bHash common.Hash) *types.Receipt {
   189  	// actually run the governance message
   190  	msg := types.NewMessage(prop.From, &prop.To, 0, prop.Value, header.GasLimit, new(big.Int), prop.Data, false)
   191  	state.Prepare(txHash, bHash, totalTxIndex)
   192  	_, err := vmcaller.ExecuteMsg(msg, state, header, newChainContext(chain, c), c.chainConfig)
   193  	state.Finalise(true)
   194  
   195  	// governance message will not actually consumes gas
   196  	receipt := types.NewReceipt([]byte{}, err != nil, header.GasUsed)
   197  	// Set the receipt logs and create a bloom for filtering
   198  	receipt.Logs = state.GetLogs(txHash)
   199  	receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
   200  
   201  	log.Info("executeProposalMsg", "action", "evmCall", "id", prop.Id.String(), "from", prop.From, "to", prop.To, "value", prop.Value.String(), "data", hexutil.Encode(prop.Data), "txHash", txHash.String(), "err", err)
   202  
   203  	return receipt
   204  }