github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/poll/consortium.go (about)

     1  package poll
     2  
     3  import (
     4  	"context"
     5  	"math/big"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/ethereum/go-ethereum/accounts/abi"
    10  	"github.com/ethereum/go-ethereum/common"
    11  	"github.com/ethereum/go-ethereum/common/hexutil"
    12  	"github.com/ethereum/go-ethereum/crypto"
    13  	"github.com/iotexproject/go-pkgs/hash"
    14  	"github.com/iotexproject/iotex-address/address"
    15  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    16  	"github.com/pkg/errors"
    17  	"go.uber.org/zap"
    18  
    19  	"github.com/iotexproject/iotex-core/action"
    20  	"github.com/iotexproject/iotex-core/action/protocol"
    21  	"github.com/iotexproject/iotex-core/action/protocol/execution/evm"
    22  	"github.com/iotexproject/iotex-core/action/protocol/rolldpos"
    23  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    24  	"github.com/iotexproject/iotex-core/pkg/log"
    25  	"github.com/iotexproject/iotex-core/state"
    26  )
    27  
    28  var (
    29  	_consortiumCommitteeContractCreator = address.ZeroAddress
    30  	_consortiumCommitteeContractNonce   = uint64(0)
    31  	// this is a special execution that is not signed, set hash = hex-string of "_consortiumCommitteeContractHash"
    32  	_consortiumCommitteeContractHash, _ = hash.HexStringToHash256("00636f6e736f727469756d436f6d6d6974746565436f6e747261637448617368")
    33  )
    34  
    35  type contractReader interface {
    36  	Read(ctx context.Context, contract string, data []byte) ([]byte, error)
    37  }
    38  
    39  type contractReaderFunc func(context.Context, string, []byte) ([]byte, error)
    40  
    41  func (f contractReaderFunc) Read(ctx context.Context, contract string, data []byte) ([]byte, error) {
    42  	return f(ctx, contract, data)
    43  }
    44  
    45  type consortiumCommittee struct {
    46  	contractReader contractReader
    47  	contract       string
    48  	abi            abi.ABI
    49  	bufferEpochNum uint64
    50  	bufferResult   state.CandidateList
    51  	indexer        *CandidateIndexer
    52  	addr           address.Address
    53  	getBlockHash   evm.GetBlockHash
    54  }
    55  
    56  // NewConsortiumCommittee creates a committee for consorium chain
    57  func NewConsortiumCommittee(indexer *CandidateIndexer, readContract ReadContract, getBlockHash evm.GetBlockHash) (Protocol, error) {
    58  	abi, err := abi.JSON(strings.NewReader(ConsortiumManagementABI))
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	if readContract == nil {
    64  		return nil, errors.New("failed to create consortium committee: empty read contract callback")
    65  	}
    66  	h := hash.Hash160b([]byte(_protocolID))
    67  	addr, err := address.FromBytes(h[:])
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	return &consortiumCommittee{
    72  		contractReader: genContractReaderFromReadContract(readContract, true),
    73  		abi:            abi,
    74  		addr:           addr,
    75  		indexer:        indexer,
    76  		getBlockHash:   getBlockHash,
    77  	}, nil
    78  }
    79  
    80  func (cc *consortiumCommittee) Start(ctx context.Context, sr protocol.StateReader) (interface{}, error) {
    81  	g := genesis.MustExtractGenesisContext(ctx)
    82  	if g.ConsortiumCommitteeContractCode == "" {
    83  		return nil, errors.New("cannot find consortium committee contract in gensis")
    84  	}
    85  
    86  	caller, _ := address.FromString(_consortiumCommitteeContractCreator)
    87  	ethAddr := crypto.CreateAddress(common.BytesToAddress(caller.Bytes()), _consortiumCommitteeContractNonce)
    88  	iotxAddr, _ := address.FromBytes(ethAddr.Bytes())
    89  	cc.contract = iotxAddr.String()
    90  	log.L().Debug("Loaded consortium committee contract", zap.String("address", iotxAddr.String()))
    91  
    92  	return nil, nil
    93  }
    94  
    95  func (cc *consortiumCommittee) CreateGenesisStates(ctx context.Context, sm protocol.StateManager) error {
    96  	g := genesis.MustExtractGenesisContext(ctx)
    97  	blkCtx := protocol.MustGetBlockCtx(ctx)
    98  	blkCtx.Producer, _ = address.FromString(_consortiumCommitteeContractCreator)
    99  	blkCtx.GasLimit = g.BlockGasLimitByHeight(0)
   100  	bytes, err := hexutil.Decode(g.ConsortiumCommitteeContractCode)
   101  	if err != nil {
   102  		return err
   103  	}
   104  	execution, err := action.NewExecution(
   105  		"",
   106  		_consortiumCommitteeContractNonce,
   107  		big.NewInt(0),
   108  		g.BlockGasLimitByHeight(0),
   109  		big.NewInt(0),
   110  		bytes,
   111  	)
   112  	if err != nil {
   113  		return err
   114  	}
   115  	actionCtx := protocol.ActionCtx{}
   116  	actionCtx.Caller, err = address.FromString(_consortiumCommitteeContractCreator)
   117  	if err != nil {
   118  		return err
   119  	}
   120  	actionCtx.Nonce = _consortiumCommitteeContractNonce
   121  	actionCtx.ActionHash = _consortiumCommitteeContractHash
   122  	actionCtx.GasPrice = execution.GasPrice()
   123  	actionCtx.IntrinsicGas, err = execution.IntrinsicGas()
   124  	if err != nil {
   125  		return err
   126  	}
   127  	ctx = protocol.WithActionCtx(ctx, actionCtx)
   128  	ctx = protocol.WithBlockCtx(ctx, blkCtx)
   129  	getBlockTime := func(u uint64) (time.Time, error) {
   130  		// make sure the returned timestamp is after the current block time so that evm upgrades based on timestamp (Shanghai and onwards) are disabled
   131  		return blkCtx.BlockTimeStamp.Add(5 * time.Second), nil
   132  	}
   133  	ctx = evm.WithHelperCtx(ctx, evm.HelperContext{
   134  		GetBlockHash: func(height uint64) (hash.Hash256, error) {
   135  			return hash.ZeroHash256, nil
   136  		},
   137  		GetBlockTime: getBlockTime,
   138  		DepositGasFunc: func(context.Context, protocol.StateManager, address.Address, *big.Int, *big.Int) (*action.TransactionLog, error) {
   139  			return nil, nil
   140  		},
   141  		Sgd: nil,
   142  	})
   143  
   144  	// deploy consortiumCommittee contract
   145  	_, receipt, err := evm.ExecuteContract(
   146  		ctx,
   147  		sm,
   148  		execution,
   149  	)
   150  	if err != nil {
   151  		return err
   152  	}
   153  	if receipt.Status != uint64(iotextypes.ReceiptStatus_Success) {
   154  		return errors.Errorf("error when deploying consortiumCommittee contract, status=%d", receipt.Status)
   155  	}
   156  	cc.contract = receipt.ContractAddress
   157  
   158  	ctx = evm.WithHelperCtx(ctx, evm.HelperContext{
   159  		GetBlockHash: cc.getBlockHash,
   160  		GetBlockTime: getBlockTime,
   161  	})
   162  	r := getContractReaderForGenesisStates(ctx, sm)
   163  	cands, err := cc.readDelegatesWithContractReader(ctx, r)
   164  	if err != nil {
   165  		return err
   166  	}
   167  
   168  	return setCandidates(ctx, sm, cc.indexer, cands, uint64(1))
   169  }
   170  
   171  func (cc *consortiumCommittee) CreatePreStates(ctx context.Context, sm protocol.StateManager) error {
   172  	return nil
   173  }
   174  
   175  func (cc *consortiumCommittee) CreatePostSystemActions(ctx context.Context, sr protocol.StateReader) ([]action.Envelope, error) {
   176  	return createPostSystemActions(ctx, sr, cc)
   177  }
   178  
   179  func (cc *consortiumCommittee) Handle(ctx context.Context, act action.Action, sm protocol.StateManager) (*action.Receipt, error) {
   180  	return handle(ctx, act, sm, cc.indexer, cc.addr.String())
   181  }
   182  
   183  func (cc *consortiumCommittee) Validate(ctx context.Context, act action.Action, sr protocol.StateReader) error {
   184  	return validate(ctx, sr, cc, act)
   185  }
   186  
   187  func (cc *consortiumCommittee) ReadState(
   188  	ctx context.Context,
   189  	sm protocol.StateReader,
   190  	method []byte,
   191  	args ...[]byte,
   192  ) ([]byte, uint64, error) {
   193  	return nil, uint64(0), nil
   194  }
   195  
   196  // Register registers the protocol with a unique ID
   197  func (cc *consortiumCommittee) Register(r *protocol.Registry) error {
   198  	return r.Register(_protocolID, cc)
   199  }
   200  
   201  // ForceRegister registers the protocol with a unique ID and force replacing the previous protocol if it exists
   202  func (cc *consortiumCommittee) ForceRegister(r *protocol.Registry) error {
   203  	return r.ForceRegister(_protocolID, cc)
   204  }
   205  
   206  // Name returns the name of protocol
   207  func (cc *consortiumCommittee) Name() string {
   208  	return _protocolID
   209  }
   210  
   211  func (cc *consortiumCommittee) CalculateCandidatesByHeight(ctx context.Context, _ protocol.StateReader, _ uint64) (state.CandidateList, error) {
   212  	return cc.readDelegates(ctx)
   213  }
   214  
   215  func (cc *consortiumCommittee) CalculateUnproductiveDelegates(
   216  	ctx context.Context,
   217  	sr protocol.StateReader,
   218  ) ([]string, error) {
   219  	return nil, nil
   220  }
   221  
   222  func (cc *consortiumCommittee) Delegates(ctx context.Context, _ protocol.StateReader) (state.CandidateList, error) {
   223  	return cc.readDelegates(ctx)
   224  }
   225  
   226  func (cc *consortiumCommittee) NextDelegates(ctx context.Context, _ protocol.StateReader) (state.CandidateList, error) {
   227  	return cc.readDelegates(ctx)
   228  }
   229  
   230  func (cc *consortiumCommittee) Candidates(ctx context.Context, _ protocol.StateReader) (state.CandidateList, error) {
   231  	return cc.readDelegates(ctx)
   232  }
   233  
   234  func (cc *consortiumCommittee) NextCandidates(ctx context.Context, _ protocol.StateReader) (state.CandidateList, error) {
   235  	return cc.readDelegates(ctx)
   236  }
   237  
   238  func (cc *consortiumCommittee) readDelegates(ctx context.Context) (state.CandidateList, error) {
   239  	return cc.readDelegatesWithContractReader(ctx, cc.contractReader)
   240  }
   241  
   242  func (cc *consortiumCommittee) readDelegatesWithContractReader(ctx context.Context, r contractReader) (state.CandidateList, error) {
   243  	bcCtx := protocol.MustGetBlockchainCtx(ctx)
   244  	rp := rolldpos.MustGetProtocol(protocol.MustGetRegistry(ctx))
   245  	epochNum := rp.GetEpochNum(bcCtx.Tip.Height)
   246  	if cc.bufferEpochNum == epochNum && cc.bufferResult != nil {
   247  		return cc.bufferResult, nil
   248  	}
   249  
   250  	data, err := cc.abi.Pack("delegates")
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	data, err = r.Read(ctx, cc.contract, data)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  	ret, err := cc.abi.Unpack("delegates", data)
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  	res, err := toEtherAddressSlice(ret[0])
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	candidates := make([]*state.Candidate, 0, len(res))
   269  	for _, ethAddr := range res {
   270  		pkhash, err := hexutil.Decode(ethAddr.String())
   271  		if err != nil {
   272  			return nil, err
   273  		}
   274  		addr, err := address.FromBytes(pkhash)
   275  		if err != nil {
   276  			return nil, err
   277  		}
   278  		candidates = append(candidates, &state.Candidate{
   279  			Address: addr.String(),
   280  			Votes:   big.NewInt(100),
   281  		})
   282  	}
   283  	cc.bufferEpochNum = epochNum
   284  	cc.bufferResult = candidates
   285  	return candidates, nil
   286  }
   287  
   288  func genContractReaderFromReadContract(r ReadContract, setting bool) contractReaderFunc {
   289  	return func(ctx context.Context, contract string, data []byte) ([]byte, error) {
   290  		return r(ctx, contract, data, setting)
   291  	}
   292  }
   293  
   294  func getContractReaderForGenesisStates(ctx context.Context, sm protocol.StateManager) contractReaderFunc {
   295  	return func(ctx context.Context, contract string, data []byte) ([]byte, error) {
   296  		gasLimit := uint64(10000000)
   297  		ex, err := action.NewExecution(contract, 1, big.NewInt(0), gasLimit, big.NewInt(0), data)
   298  		if err != nil {
   299  			return nil, err
   300  		}
   301  
   302  		addr, err := address.FromString(address.ZeroAddress)
   303  		if err != nil {
   304  			return nil, err
   305  		}
   306  
   307  		res, _, err := evm.SimulateExecution(ctx, sm, addr, ex)
   308  
   309  		return res, err
   310  	}
   311  }
   312  
   313  func toEtherAddressSlice(v interface{}) ([]common.Address, error) {
   314  	if addr, ok := v.([]common.Address); ok {
   315  		return addr, nil
   316  	}
   317  	return nil, ErrWrongData
   318  }