Zanzibar (IIP-59) support: the two new staking actions, and the BLS public key plus proof of possession on candidate register/update.
New
SetVoterRewardOptInMethod / SetVoterRewardOptInRequest — opts a delegate into protocol-native voter reward distribution (ActionCore field 57). No payload; the sender is the delegate. It is one-way — the protocol has no counterpart action to opt back out.
Opting in with no reward portions published on the DelegateProfile contract is silently harmful rather than rejected: the protocol cannot tell "unset" from "explicit zero", falls back to 100% commission, and pays every voter zero while producing successful distributions.
SetVoterRewardDestinationMethod / SetVoterRewardDestinationRequest — redirects the sender's own voter rewards (field 58). Passing your own address clears the override.
blsPubKey / blsPop on CandidateRegisterRequest and CandidateUpdateRequest — hex, with or without a 0x prefix. Optional: leave them null to register or update without a key, and to leave an already registered key untouched. The proof must be over the candidate's owner address; Zanzibar rejects it otherwise with ErrUnauthorizedOperator (203) on the receipt.
Three behaviours worth knowing:
- A
0xprefix is stripped.Numeric.hexStringToByteArraydoes not do it — it reads'0'and'x'as a byte, andCharacter.digit('x', 16)is-1— so a prefixed key would decode to something corrupt of a plausible length, sign cleanly, and be rejected on chain with no indication why. Every tool that emits a BLS key quotes it with the prefix. - Absent stays absent. A null or empty field is left unset rather than set to empty bytes, so an update that does not mention a key leaves an already registered one alone.
- A proof with no key throws. That pairing is invalid in every fork era and the chain rejects it at validation, before a receipt exists, so the caller would otherwise get a bare RPC error with no action hash to look up.
Proto
action.proto picks up SetVoterRewardOptIn (57), SetVoterRewardDestination (58), CandidateDeactivate (54), ScheduleCandidateDeactivation (55), and blsPubKey / blsPop on CandidateBasicInfo. Every message and field number was checked against iotex-proto v0.6.13. Field 56 (SetCommissionRate) is deliberately omitted: no iotex-core release implements it, and a node answers no applicable action to handle proto type.
Envelop detects the opt-in by presence rather than payload length — it serializes to zero bytes, so a length check drops it silently and turns a delegate's one-way action into a no-op.
Also since v0.8.1: CI bumped to actions/checkout@v5 / setup-java@v5, and bcprov-jdk15to18 1.71 → 1.74.
Verification
Against a local four-delegate chain running iotex-core v2.5.0 with Zanzibar, Beta and Gamma active:
- both IIP-59 actions accepted on chain
- BLS: a proof bound to the candidate settles status 1; one bound to a different candidate settles 203. The pair is what separates "the chain accepted this" from "the chain verified this"
- voter reward payout transaction logs decode correctly. IIP-59 adds no new
TransactionLogType— payouts reuseCLAIM_FROM_REWARDING_FUND— but the sender is the protocol pool pseudo-addressio0000000000000000000000rewardingprotocol, which has no 20-byte hash behind it and comes back empty from anything that decodes an address the ordinary way
Live tests skip unless IOTEX_TEST_KEY is set. IOTEX_TEST_ENDPOINT / IOTEX_TEST_CHAINID / IOTEX_TEST_SECURE aim them at a local chain; the defaults still point at TestNet.