github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/rewarding/protocol.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package rewarding 7 8 import ( 9 "context" 10 "math/big" 11 12 "github.com/pkg/errors" 13 "go.uber.org/zap" 14 15 "github.com/iotexproject/go-pkgs/hash" 16 "github.com/iotexproject/iotex-address/address" 17 "github.com/iotexproject/iotex-proto/golang/iotextypes" 18 19 "github.com/iotexproject/iotex-core/action" 20 "github.com/iotexproject/iotex-core/action/protocol" 21 accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util" 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 const ( 29 // TODO: it works only for one instance per protocol definition now 30 _protocolID = "rewarding" 31 _v2RewardingNamespace = "Rewarding" 32 ) 33 34 var ( 35 _adminKey = []byte("adm") 36 _fundKey = []byte("fnd") 37 _blockRewardHistoryKeyPrefix = []byte("brh") 38 _epochRewardHistoryKeyPrefix = []byte("erh") 39 _accountKeyPrefix = []byte("acc") 40 _exemptKey = []byte("xpt") 41 errInvalidEpoch = errors.New("invalid start/end epoch number") 42 ) 43 44 // Protocol defines the protocol of the rewarding fund and the rewarding process. It allows the admin to config the 45 // reward amount, users to donate tokens to the fund, block producers to grant them block and epoch reward and, 46 // beneficiaries to claim the balance into their personal account. 47 type Protocol struct { 48 keyPrefix []byte 49 addr address.Address 50 cfg genesis.Rewarding 51 } 52 53 // NewProtocol instantiates a rewarding protocol instance. 54 func NewProtocol(cfg genesis.Rewarding) *Protocol { 55 h := hash.Hash160b([]byte(_protocolID)) 56 addr, err := address.FromBytes(h[:]) 57 if err != nil { 58 log.L().Panic("Error when constructing the address of rewarding protocol", zap.Error(err)) 59 } 60 if err = validateFoundationBonusExtension(cfg); err != nil { 61 log.L().Panic("failed to validate foundation bonus extension", zap.Error(err)) 62 } 63 return &Protocol{ 64 keyPrefix: h[:], 65 addr: addr, 66 cfg: cfg, 67 } 68 } 69 70 // ProtocolAddr returns the address generated from protocol id 71 func ProtocolAddr() address.Address { 72 return protocol.HashStringToAddress(_protocolID) 73 } 74 75 // verify that foundation bonus extension epochs are in increasing order 76 func validateFoundationBonusExtension(cfg genesis.Rewarding) error { 77 if cfg.FoundationBonusP2StartEpoch > 0 || cfg.FoundationBonusP2EndEpoch > 0 { 78 if cfg.FoundationBonusP2StartEpoch < cfg.FoundationBonusLastEpoch || cfg.FoundationBonusP2EndEpoch < cfg.FoundationBonusP2StartEpoch { 79 return errInvalidEpoch 80 } 81 } 82 return nil 83 } 84 85 // FindProtocol finds the registered protocol from registry 86 func FindProtocol(registry *protocol.Registry) *Protocol { 87 if registry == nil { 88 return nil 89 } 90 p, ok := registry.Find(_protocolID) 91 if !ok { 92 return nil 93 } 94 rp, ok := p.(*Protocol) 95 if !ok { 96 log.S().Panic("fail to cast reward protocol") 97 } 98 return rp 99 } 100 101 // CreatePreStates updates state manager 102 func (p *Protocol) CreatePreStates(ctx context.Context, sm protocol.StateManager) error { 103 g := genesis.MustExtractGenesisContext(ctx) 104 blkCtx := protocol.MustGetBlockCtx(ctx) 105 switch blkCtx.BlockHeight { 106 case g.AleutianBlockHeight: 107 return p.SetReward(ctx, sm, g.AleutianEpochReward(), false) 108 case g.DardanellesBlockHeight: 109 return p.SetReward(ctx, sm, g.DardanellesBlockReward(), true) 110 case g.GreenlandBlockHeight: 111 return p.migrateValueGreenland(ctx, sm) 112 case g.KamchatkaBlockHeight: 113 return p.setFoundationBonusExtension(ctx, sm) 114 } 115 return nil 116 } 117 118 func (p *Protocol) migrateValueGreenland(_ context.Context, sm protocol.StateManager) error { 119 if err := p.migrateValue(sm, _adminKey, &admin{}); err != nil { 120 return err 121 } 122 if err := p.migrateValue(sm, _fundKey, &fund{}); err != nil { 123 return err 124 } 125 return p.migrateValue(sm, _exemptKey, &exempt{}) 126 } 127 128 func (p *Protocol) migrateValue(sm protocol.StateManager, key []byte, value interface{}) error { 129 if _, err := p.stateV1(sm, key, value); err != nil { 130 if errors.Cause(err) == state.ErrStateNotExist { 131 // doesn't exist now just skip migration 132 return nil 133 } 134 return err 135 } 136 if err := p.putStateV2(sm, key, value); err != nil { 137 return err 138 } 139 return p.deleteStateV1(sm, key) 140 } 141 142 func (p *Protocol) setFoundationBonusExtension(ctx context.Context, sm protocol.StateManager) error { 143 a := admin{} 144 if _, err := p.state(ctx, sm, _adminKey, &a); err != nil { 145 return err 146 } 147 148 rp := rolldpos.MustGetProtocol(protocol.MustGetRegistry(ctx)) 149 blkCtx := protocol.MustGetBlockCtx(ctx) 150 newLastEpoch := rp.GetEpochNum(blkCtx.BlockHeight) + 8760 151 152 if a.foundationBonusLastEpoch < p.cfg.FoundationBonusP2EndEpoch { 153 a.foundationBonusLastEpoch = p.cfg.FoundationBonusP2EndEpoch 154 } 155 if a.foundationBonusLastEpoch < newLastEpoch { 156 a.foundationBonusLastEpoch = newLastEpoch 157 } 158 return p.putState(ctx, sm, _adminKey, &a) 159 } 160 161 // CreatePostSystemActions creates a list of system actions to be appended to block actions 162 func (p *Protocol) CreatePostSystemActions(ctx context.Context, _ protocol.StateReader) ([]action.Envelope, error) { 163 blkCtx := protocol.MustGetBlockCtx(ctx) 164 grants := []action.Envelope{createGrantRewardAction(action.BlockReward, blkCtx.BlockHeight)} 165 rp := rolldpos.FindProtocol(protocol.MustGetRegistry(ctx)) 166 if rp != nil && blkCtx.BlockHeight == rp.GetEpochLastBlockHeight(rp.GetEpochNum(blkCtx.BlockHeight)) { 167 grants = append(grants, createGrantRewardAction(action.EpochReward, blkCtx.BlockHeight)) 168 } 169 170 return grants, nil 171 } 172 173 func createGrantRewardAction(rewardType int, height uint64) action.Envelope { 174 builder := action.EnvelopeBuilder{} 175 gb := action.GrantRewardBuilder{} 176 grant := gb.SetRewardType(rewardType).SetHeight(height).Build() 177 178 return builder.SetNonce(0). 179 SetGasPrice(big.NewInt(0)). 180 SetGasLimit(grant.GasLimit()). 181 SetAction(&grant). 182 Build() 183 } 184 185 // Validate validates a reward action 186 func (p *Protocol) Validate(ctx context.Context, act action.Action, sr protocol.StateReader) error { 187 switch act.(type) { 188 case *action.GrantReward: 189 actionCtx := protocol.MustGetActionCtx(ctx) 190 if !address.Equal(protocol.MustGetBlockCtx(ctx).Producer, actionCtx.Caller) { 191 return errors.New("Only producer could create reward") 192 } 193 if actionCtx.GasPrice != nil && actionCtx.GasPrice.Cmp(big.NewInt(0)) != 0 || actionCtx.IntrinsicGas != 0 { 194 return errors.New("invalid gas price or intrinsic gas for reward action") 195 } 196 } 197 return nil 198 } 199 200 // Handle handles the actions on the rewarding protocol 201 func (p *Protocol) Handle( 202 ctx context.Context, 203 act action.Action, 204 sm protocol.StateManager, 205 ) (*action.Receipt, error) { 206 // TODO: simplify the boilerplate 207 switch act := act.(type) { 208 case *action.DepositToRewardingFund: 209 si := sm.Snapshot() 210 rlog, err := p.Deposit(ctx, sm, act.Amount(), iotextypes.TransactionLogType_DEPOSIT_TO_REWARDING_FUND) 211 if err != nil { 212 log.L().Debug("Error when handling rewarding action", zap.Error(err)) 213 return p.settleUserAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil) 214 } 215 return p.settleUserAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, nil, rlog) 216 case *action.ClaimFromRewardingFund: 217 si := sm.Snapshot() 218 rlog, err := p.Claim(ctx, sm, act.Amount()) 219 if err != nil { 220 log.L().Debug("Error when handling rewarding action", zap.Error(err)) 221 return p.settleUserAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil) 222 } 223 return p.settleUserAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, nil, rlog) 224 case *action.GrantReward: 225 switch act.RewardType() { 226 case action.BlockReward: 227 si := sm.Snapshot() 228 rewardLog, err := p.GrantBlockReward(ctx, sm) 229 if err != nil { 230 log.L().Debug("Error when handling rewarding action", zap.Error(err)) 231 return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil) 232 } 233 if rewardLog == nil { 234 return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, nil) 235 } 236 return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, []*action.Log{rewardLog}) 237 case action.EpochReward: 238 si := sm.Snapshot() 239 rewardLogs, err := p.GrantEpochReward(ctx, sm) 240 if err != nil { 241 log.L().Debug("Error when handling rewarding action", zap.Error(err)) 242 return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil) 243 } 244 return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, rewardLogs) 245 } 246 } 247 return nil, nil 248 } 249 250 // ReadState read the state on blockchain via protocol 251 func (p *Protocol) ReadState( 252 ctx context.Context, 253 sr protocol.StateReader, 254 method []byte, 255 args ...[]byte, 256 ) ([]byte, uint64, error) { 257 switch string(method) { 258 case "AvailableBalance": 259 balance, height, err := p.AvailableBalance(ctx, sr) 260 if err != nil { 261 return nil, uint64(0), err 262 } 263 return []byte(balance.String()), height, nil 264 case "TotalBalance": 265 balance, height, err := p.TotalBalance(ctx, sr) 266 if err != nil { 267 return nil, uint64(0), err 268 } 269 return []byte(balance.String()), height, nil 270 case "UnclaimedBalance": 271 if len(args) != 1 { 272 return nil, uint64(0), errors.Errorf("invalid number of arguments %d", len(args)) 273 } 274 addr, err := address.FromString(string(args[0])) 275 if err != nil { 276 return nil, uint64(0), err 277 } 278 balance, height, err := p.UnclaimedBalance(ctx, sr, addr) 279 if err != nil { 280 return nil, uint64(0), err 281 } 282 return []byte(balance.String()), height, nil 283 default: 284 return nil, uint64(0), errors.New("corresponding method isn't found") 285 } 286 } 287 288 // Register registers the protocol with a unique ID 289 func (p *Protocol) Register(r *protocol.Registry) error { 290 return r.Register(_protocolID, p) 291 } 292 293 // ForceRegister registers the protocol with a unique ID and force replacing the previous protocol if it exists 294 func (p *Protocol) ForceRegister(r *protocol.Registry) error { 295 return r.ForceRegister(_protocolID, p) 296 } 297 298 // Name returns the name of protocol 299 func (p *Protocol) Name() string { 300 return _protocolID 301 } 302 303 // useV2Storage return true after greenland when we start using v2 storage. 304 func useV2Storage(ctx context.Context) bool { 305 return protocol.MustGetFeatureCtx(ctx).UseV2Storage 306 } 307 308 func (p *Protocol) state(ctx context.Context, sm protocol.StateReader, key []byte, value interface{}) (uint64, error) { 309 h, _, err := p.stateCheckLegacy(ctx, sm, key, value) 310 return h, err 311 } 312 313 func (p *Protocol) stateCheckLegacy(ctx context.Context, sm protocol.StateReader, key []byte, value interface{}) (uint64, bool, error) { 314 if useV2Storage(ctx) { 315 h, err := p.stateV2(sm, key, value) 316 if errors.Cause(err) != state.ErrStateNotExist { 317 return h, false, err 318 } 319 } 320 h, err := p.stateV1(sm, key, value) 321 return h, true, err 322 } 323 324 func (p *Protocol) stateV1(sm protocol.StateReader, key []byte, value interface{}) (uint64, error) { 325 keyHash := hash.Hash160b(append(p.keyPrefix, key...)) 326 return sm.State(value, protocol.LegacyKeyOption(keyHash)) 327 } 328 329 func (p *Protocol) stateV2(sm protocol.StateReader, key []byte, value interface{}) (uint64, error) { 330 k := append(p.keyPrefix, key...) 331 return sm.State(value, protocol.KeyOption(k), protocol.NamespaceOption(_v2RewardingNamespace)) 332 } 333 334 func (p *Protocol) putState(ctx context.Context, sm protocol.StateManager, key []byte, value interface{}) error { 335 if useV2Storage(ctx) { 336 return p.putStateV2(sm, key, value) 337 } 338 return p.putStateV1(sm, key, value) 339 } 340 341 func (p *Protocol) putStateV1(sm protocol.StateManager, key []byte, value interface{}) error { 342 keyHash := hash.Hash160b(append(p.keyPrefix, key...)) 343 _, err := sm.PutState(value, protocol.LegacyKeyOption(keyHash)) 344 return err 345 } 346 347 func (p *Protocol) putStateV2(sm protocol.StateManager, key []byte, value interface{}) error { 348 k := append(p.keyPrefix, key...) 349 _, err := sm.PutState(value, protocol.KeyOption(k), protocol.NamespaceOption(_v2RewardingNamespace)) 350 return err 351 } 352 353 func (p *Protocol) deleteState(ctx context.Context, sm protocol.StateManager, key []byte) error { 354 if useV2Storage(ctx) { 355 return p.deleteStateV2(sm, key) 356 } 357 return p.deleteStateV1(sm, key) 358 } 359 360 func (p *Protocol) deleteStateV1(sm protocol.StateManager, key []byte) error { 361 keyHash := hash.Hash160b(append(p.keyPrefix, key...)) 362 _, err := sm.DelState(protocol.LegacyKeyOption(keyHash)) 363 if errors.Cause(err) == state.ErrStateNotExist { 364 // don't care if not exist 365 return nil 366 } 367 return err 368 } 369 370 func (p *Protocol) deleteStateV2(sm protocol.StateManager, key []byte) error { 371 k := append(p.keyPrefix, key...) 372 _, err := sm.DelState(protocol.KeyOption(k), protocol.NamespaceOption(_v2RewardingNamespace)) 373 if errors.Cause(err) == state.ErrStateNotExist { 374 // don't care if not exist 375 return nil 376 } 377 return err 378 } 379 380 func (p *Protocol) settleSystemAction( 381 ctx context.Context, 382 sm protocol.StateManager, 383 status uint64, 384 si int, 385 logs []*action.Log, 386 tLogs ...*action.TransactionLog, 387 ) (*action.Receipt, error) { 388 return p.settleAction(ctx, sm, status, si, true, logs, tLogs...) 389 } 390 391 func (p *Protocol) settleUserAction( 392 ctx context.Context, 393 sm protocol.StateManager, 394 status uint64, 395 si int, 396 logs []*action.Log, 397 tLogs ...*action.TransactionLog, 398 ) (*action.Receipt, error) { 399 return p.settleAction(ctx, sm, status, si, false, logs, tLogs...) 400 } 401 402 func (p *Protocol) settleAction( 403 ctx context.Context, 404 sm protocol.StateManager, 405 status uint64, 406 si int, 407 isSystemAction bool, 408 logs []*action.Log, 409 tLogs ...*action.TransactionLog, 410 ) (*action.Receipt, error) { 411 actionCtx := protocol.MustGetActionCtx(ctx) 412 blkCtx := protocol.MustGetBlockCtx(ctx) 413 if status == uint64(iotextypes.ReceiptStatus_Failure) { 414 if err := sm.Revert(si); err != nil { 415 return nil, err 416 } 417 } 418 skipUpdateForSystemAction := protocol.MustGetFeatureCtx(ctx).FixGasAndNonceUpdate 419 if !isSystemAction || !skipUpdateForSystemAction { 420 gasFee := big.NewInt(0).Mul(actionCtx.GasPrice, big.NewInt(0).SetUint64(actionCtx.IntrinsicGas)) 421 depositLog, err := DepositGas(ctx, sm, gasFee) 422 if err != nil { 423 return nil, err 424 } 425 if depositLog != nil { 426 tLogs = append(tLogs, depositLog) 427 } 428 if err := p.increaseNonce( 429 ctx, 430 sm, 431 actionCtx.Caller, 432 actionCtx.Nonce, 433 !skipUpdateForSystemAction && actionCtx.Nonce == 0, 434 ); err != nil { 435 return nil, err 436 } 437 } 438 439 return p.createReceipt(status, blkCtx.BlockHeight, actionCtx.ActionHash, actionCtx.IntrinsicGas, logs, tLogs...), nil 440 } 441 442 func (p *Protocol) increaseNonce(ctx context.Context, sm protocol.StateManager, addr address.Address, nonce uint64, skipSetNonce bool) error { 443 accountCreationOpts := []state.AccountCreationOption{} 444 if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { 445 accountCreationOpts = append(accountCreationOpts, state.LegacyNonceAccountTypeOption()) 446 } 447 acc, err := accountutil.LoadOrCreateAccount(sm, addr, accountCreationOpts...) 448 if err != nil { 449 return err 450 } 451 if !skipSetNonce { 452 if err := acc.SetPendingNonce(nonce + 1); err != nil { 453 return errors.Wrapf(err, "invalid nonce %d", nonce) 454 } 455 } 456 return accountutil.StoreAccount(sm, addr, acc) 457 } 458 459 func (p *Protocol) createReceipt( 460 status uint64, 461 blkHeight uint64, 462 actHash hash.Hash256, 463 gasConsumed uint64, 464 logs []*action.Log, 465 tLogs ...*action.TransactionLog, 466 ) *action.Receipt { 467 // TODO: need to review the fields 468 return (&action.Receipt{ 469 Status: status, 470 BlockHeight: blkHeight, 471 ActionHash: actHash, 472 GasConsumed: gasConsumed, 473 ContractAddress: p.addr.String(), 474 }).AddLogs(logs...).AddTransactionLogs(tLogs...) 475 }