Files
ark/common/bip68.go
Louis Singer 02542c3634 Support forfeit with CHECKLOCKTIMEVERIFY (#389)
* explicit Timelock struct

* support & test CLTV forfeit path

* fix wasm pkg

* fix wasm

* fix liquid GetCurrentBlockTime

* cleaning

* move esplora URL check
2024-11-28 14:51:06 +01:00

97 lines
2.3 KiB
Go

package common
import (
"fmt"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/txscript"
)
const (
SEQUENCE_LOCKTIME_MASK = 0x0000ffff
SEQUENCE_LOCKTIME_TYPE_FLAG = 1 << 22
SEQUENCE_LOCKTIME_GRANULARITY = 9
SECONDS_MOD = 1 << SEQUENCE_LOCKTIME_GRANULARITY
SECONDS_MAX = SEQUENCE_LOCKTIME_MASK << SEQUENCE_LOCKTIME_GRANULARITY
SEQUENCE_LOCKTIME_DISABLE_FLAG = 1 << 31
SECONDS_PER_BLOCK = 10 * 60 // 10 minutes
)
type LocktimeType uint
const (
LocktimeTypeSecond LocktimeType = iota
LocktimeTypeBlock
)
// Locktime represents a BIP68 relative timelock value.
// This struct is comparable and can be used as a map key.
type Locktime struct {
Type LocktimeType
Value uint32
}
func (l Locktime) Seconds() int64 {
if l.Type == LocktimeTypeBlock {
return int64(l.Value) * SECONDS_PER_BLOCK
}
return int64(l.Value)
}
func (l Locktime) Compare(other Locktime) int {
val := l.Seconds()
otherVal := other.Seconds()
if val == otherVal {
return 0
}
if val < otherVal {
return -1
}
return 1
}
// LessThan returns true if this locktime is less than the other locktime
func (l Locktime) LessThan(other Locktime) bool {
return l.Compare(other) < 0
}
func BIP68Sequence(locktime Locktime) (uint32, error) {
value := locktime.Value
isSeconds := locktime.Type == LocktimeTypeSecond
if isSeconds {
if value > SECONDS_MAX {
return 0, fmt.Errorf("seconds too large, max is %d", SECONDS_MAX)
}
if value%SECONDS_MOD != 0 {
return 0, fmt.Errorf("seconds must be a multiple of %d", SECONDS_MOD)
}
}
return blockchain.LockTimeToSequence(isSeconds, value), nil
}
func BIP68DecodeSequence(sequence []byte) (*Locktime, error) {
scriptNumber, err := txscript.MakeScriptNum(sequence, true, len(sequence))
if err != nil {
return nil, err
}
if scriptNumber >= txscript.OP_1 && scriptNumber <= txscript.OP_16 {
scriptNumber = scriptNumber - (txscript.OP_1 - 1)
}
asNumber := int64(scriptNumber)
if asNumber&SEQUENCE_LOCKTIME_DISABLE_FLAG != 0 {
return nil, fmt.Errorf("sequence is disabled")
}
if asNumber&SEQUENCE_LOCKTIME_TYPE_FLAG != 0 {
seconds := asNumber & SEQUENCE_LOCKTIME_MASK << SEQUENCE_LOCKTIME_GRANULARITY
return &Locktime{Type: LocktimeTypeSecond, Value: uint32(seconds)}, nil
}
return &Locktime{Type: LocktimeTypeBlock, Value: uint32(asNumber)}, nil
}