Skip to content

Commit 8f3a1a5

Browse files
authored
fix(ledger): conway zero withdrawal (#1904)
Signed-off-by: Chris Gianelloni <wolf31o2@blinklabs.io>
1 parent 76c7803 commit 8f3a1a5

2 files changed

Lines changed: 76 additions & 8 deletions

File tree

ledger/conway/rules.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2520,8 +2520,9 @@ func UtxoValidateDelegation(
25202520
// UtxoValidateWithdrawals validates withdrawals against ledger state.
25212521
// For phase-2 invalid transactions (IsValid=false), withdrawal validation is
25222522
// skipped since their effects are reverted and only collateral rules apply.
2523-
// PV10 and PV11 also require each withdrawing stake credential to have a DRep
2524-
// vote delegation. PV12 removes that requirement per CIP-181.
2523+
// PV10 and PV11 also require each stake credential withdrawing a non-zero
2524+
// amount to have a DRep vote delegation. PV12 removes that requirement per
2525+
// CIP-181.
25252526
func UtxoValidateWithdrawals(
25262527
tx common.Transaction,
25272528
slot uint64,
@@ -2534,7 +2535,8 @@ func UtxoValidateWithdrawals(
25342535
if err := shelley.UtxoValidateWithdrawals(tx, slot, ls, pp); err != nil {
25352536
return err
25362537
}
2537-
if len(tx.Withdrawals()) == 0 {
2538+
withdrawals := tx.Withdrawals()
2539+
if len(withdrawals) == 0 {
25382540
return nil
25392541
}
25402542
versionedPparams, ok := pp.(interface {
@@ -2548,11 +2550,18 @@ func UtxoValidateWithdrawals(
25482550
protocolMajor >= common.ProtocolVersionDijkstra {
25492551
return nil
25502552
}
2551-
delegationState, ok := ls.(common.DRepDelegationState)
2552-
if !ok {
2553-
return DRepDelegationStateUnavailableError{}
2554-
}
2555-
for addr := range tx.Withdrawals() {
2553+
var delegationState common.DRepDelegationState
2554+
for addr, amount := range withdrawals {
2555+
if amount == nil || amount.Sign() == 0 {
2556+
continue
2557+
}
2558+
if delegationState == nil {
2559+
var ok bool
2560+
delegationState, ok = ls.(common.DRepDelegationState)
2561+
if !ok {
2562+
return DRepDelegationStateUnavailableError{}
2563+
}
2564+
}
25562565
credential, ok := addr.StakeCredential()
25572566
if !ok {
25582567
continue

ledger/conway/rules_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,65 @@ func TestUtxoValidateWithdrawals_DRepDelegationProtocolGate(t *testing.T) {
138138
))
139139
})
140140

141+
for _, major := range []uint{
142+
common.ProtocolVersionPlomin,
143+
common.ProtocolVersionVanRossem,
144+
} {
145+
t.Run(fmt.Sprintf("PV%d zero amount", major), func(t *testing.T) {
146+
zeroTx := *tx
147+
zeroTx.Body.TxWithdrawals = map[*common.Address]uint64{
148+
&rewardAddr: 0,
149+
}
150+
pp := &conway.ConwayProtocolParameters{
151+
ProtocolVersion: common.ProtocolParametersProtocolVersion{
152+
Major: major,
153+
},
154+
}
155+
require.NoError(t, conway.UtxoValidateWithdrawals(
156+
&zeroTx,
157+
0,
158+
struct{ common.LedgerState }{LedgerState: baseState},
159+
pp,
160+
))
161+
})
162+
}
163+
164+
t.Run("PV10 mixed amounts checks non-zero withdrawal", func(t *testing.T) {
165+
otherStakeKeyHash := common.Blake2b224Hash(
166+
[]byte("other-withdrawal-stake-key"),
167+
)
168+
otherRewardAddr := makeConwayRewardAddress(t, otherStakeKeyHash)
169+
mixedTx := *tx
170+
mixedTx.Body.TxWithdrawals = map[*common.Address]uint64{
171+
&rewardAddr: 0,
172+
&otherRewardAddr: 1_000_000,
173+
}
174+
mixedState := mockledger.NewLedgerStateBuilder().
175+
WithRewardAccountBalance(stakeKeyHash, 1_000_000).
176+
WithRewardAccountBalance(otherStakeKeyHash, 1_000_000).
177+
WithDRepDelegation(func(
178+
credential common.Credential,
179+
) (*common.Drep, error) {
180+
assert.Equal(t, otherStakeKeyHash, credential.Credential)
181+
return nil, nil
182+
}).
183+
Build()
184+
pp := &conway.ConwayProtocolParameters{
185+
ProtocolVersion: common.ProtocolParametersProtocolVersion{
186+
Major: common.ProtocolVersionPlomin,
187+
},
188+
}
189+
err := conway.UtxoValidateWithdrawals(
190+
&mixedTx,
191+
0,
192+
mixedState,
193+
pp,
194+
)
195+
var target conway.WithdrawalNotDelegatedToDRepError
196+
require.ErrorAs(t, err, &target)
197+
assert.Equal(t, otherRewardAddr, target.RewardAddress)
198+
})
199+
141200
t.Run("PV10 requires delegation lookup support", func(t *testing.T) {
142201
pp := &conway.ConwayProtocolParameters{
143202
ProtocolVersion: common.ProtocolParametersProtocolVersion{

0 commit comments

Comments
 (0)