Skip to content

Commit

Permalink
Merge pull request #236 from SiaFoundation/large-policies
Browse files Browse the repository at this point in the history
types: Move complexity check to (SpendPolicy).Verify
  • Loading branch information
n8maninger authored Nov 27, 2024
2 parents 611d481 + bf93a07 commit d3d9f55
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 146 deletions.
9 changes: 1 addition & 8 deletions types/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -1113,10 +1113,7 @@ func (txn *Transaction) DecodeFrom(d *Decoder) {

// DecodeFrom implements types.DecoderFrom.
func (p *SpendPolicy) DecodeFrom(d *Decoder) {
const (
version = 1
maxPolicies = 1024
)
const version = 1
const (
opInvalid = iota
opAbove
Expand All @@ -1128,7 +1125,6 @@ func (p *SpendPolicy) DecodeFrom(d *Decoder) {
opUnlockConditions
)

var totalPolicies int
var readPolicy func() (SpendPolicy, error)
readPolicy = func() (SpendPolicy, error) {
switch op := d.ReadUint8(); op {
Expand All @@ -1147,9 +1143,6 @@ func (p *SpendPolicy) DecodeFrom(d *Decoder) {
case opThreshold:
n := d.ReadUint8()
of := make([]SpendPolicy, d.ReadUint8())
if totalPolicies += len(of); totalPolicies > maxPolicies {
return SpendPolicy{}, errors.New("policy is too complex")
}
var err error
for i := range of {
if of[i], err = readPolicy(); err != nil {
Expand Down
39 changes: 25 additions & 14 deletions types/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,47 +132,58 @@ func (p SpendPolicy) Verify(height uint64, medianTimestamp time.Time, sigHash Ha
}
return
}
errInvalidSignature := errors.New("invalid signature")
errInvalidPreimage := errors.New("invalid preimage")
const maxPolicies = 1024
var totalPolicies int
errOpaque := errors.New("opaque policy")
var verify func(SpendPolicy) error
verify = func(p SpendPolicy) error {
switch p := p.Type.(type) {
case PolicyTypeAbove:
if height >= uint64(p) {
return nil
}
return fmt.Errorf("height not above %v", uint64(p))
return fmt.Errorf("height (%v) not above %v", height, uint64(p))
case PolicyTypeAfter:
if medianTimestamp.After(time.Time(p)) {
return nil
}
return fmt.Errorf("median timestamp not after %v", time.Time(p))
return fmt.Errorf("median timestamp (%v) not after %v", medianTimestamp, time.Time(p))
case PolicyTypePublicKey:
if sig, ok := nextSig(); ok && PublicKey(p).VerifyHash(sigHash, sig) {
return nil
}
return errInvalidSignature
return errors.New("invalid signature")
case PolicyTypeHash:
if preimage, ok := nextPreimage(); ok && p == sha256.Sum256(preimage[:]) {
return nil
}
return errInvalidPreimage
return errors.New("invalid preimage")
case PolicyTypeThreshold:
for i := 0; i < len(p.Of) && p.N > 0 && len(p.Of[i:]) >= int(p.N); i++ {
if _, ok := p.Of[i].Type.(PolicyTypeUnlockConditions); ok {
if totalPolicies += len(p.Of); totalPolicies > maxPolicies || len(p.Of) > 255 {
return errors.New("policy is too complex")
}
var satisfied uint8
for _, sp := range p.Of {
switch sp.Type.(type) {
case PolicyTypeUnlockConditions:
return errors.New("unlock conditions cannot be sub-policies")
} else if err := verify(p.Of[i]); err == errInvalidSignature || err == errInvalidPreimage {
return err // fatal; should have been opaque
} else if err == nil {
p.N--
case PolicyTypeOpaque:
continue
default:
if satisfied == p.N {
return errors.New("threshold exceeded")
} else if err := verify(sp); err != nil {
return err // fatal; should have been opaque
}
satisfied++
}
}
if p.N == 0 {
if satisfied == p.N {
return nil
}
return errors.New("threshold not reached")
case PolicyTypeOpaque:
return errors.New("opaque policy")
return errOpaque
case PolicyTypeUnlockConditions:
if err := verify(PolicyAbove(p.Timelock)); err != nil {
return err
Expand Down
Loading

0 comments on commit d3d9f55

Please sign in to comment.