From 98a554989e81d26fd6b345c9ed7d48372a0da803 Mon Sep 17 00:00:00 2001 From: Runchao Han Date: Tue, 28 Nov 2023 10:55:51 +1100 Subject: [PATCH] chore: cleaning up btcstaking tooling library (#122) --- btcstaking/{scripts.go => scripts_utils.go} | 29 ++- btcstaking/staking.go | 105 ++++------ btcstaking/staking_test.go | 34 ++- btcstaking/staking_utils.go | 219 -------------------- btcstaking/{btc_staking.go => types.go} | 171 ++++++++++++++- go.mod | 2 +- x/btcstaking/types/types.go | 9 - 7 files changed, 253 insertions(+), 316 deletions(-) rename btcstaking/{scripts.go => scripts_utils.go} (79%) delete mode 100644 btcstaking/staking_utils.go rename btcstaking/{btc_staking.go => types.go} (63%) diff --git a/btcstaking/scripts.go b/btcstaking/scripts_utils.go similarity index 79% rename from btcstaking/scripts.go rename to btcstaking/scripts_utils.go index 41f7eac7c..e9e2a2928 100644 --- a/btcstaking/scripts.go +++ b/btcstaking/scripts_utils.go @@ -3,15 +3,16 @@ package btcstaking import ( "bytes" "fmt" + "sort" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/txscript" ) -// private helper to build multisig script +// private helper to assemble multisig script // SCRIPT: OP_CHEKCSIG OP_CHECKSIGADD OP_CHECKSIGADD ... OP_CHECKSIGADD OP_GREATERTHANOREQUAL OP_VERIFY -func buildMultiSigScript( +func assembleMultiSigScript( pubkeys []*btcec.PublicKey, threshold uint32, withVerify bool, @@ -36,6 +37,18 @@ func buildMultiSigScript( return builder.Script() } +// sortKeys takes a set of schnorr public keys and returns a new slice that is +// a copy of the keys sorted in lexicographical order bytes on the x-only +// pubkey serialization. +func sortKeys(keys []*btcec.PublicKey) []*btcec.PublicKey { + sort.SliceStable(keys, func(i, j int) bool { + keyIBytes := schnorr.SerializePubKey(keys[i]) + keyJBytes := schnorr.SerializePubKey(keys[j]) + return bytes.Compare(keyIBytes, keyJBytes) == -1 + }) + return keys +} + // prepareKeys prepares keys to be used in multisig script // Validates: // - whether there are at lest 2 keys @@ -57,11 +70,11 @@ func prepareKeysForMultisigScript(keys []*btcec.PublicKey) ([]*btcec.PublicKey, return sortedKeys, nil } -// BuildMultiSigScript creates multisig script with given keys and signer threshold to +// buildMultiSigScript creates multisig script with given keys and signer threshold to // successfully execute script // it validates whether provided keys are unique and the threshold is not greater than number of keys // If there is only one key provided it will return single key sig script -func BuildMultiSigScript( +func buildMultiSigScript( keys []*btcec.PublicKey, threshold uint32, withVerify bool, @@ -76,7 +89,7 @@ func BuildMultiSigScript( if len(keys) == 1 { // if we have only one key we can use single key sig script - return BuildSingleKeySigScript(keys[0], withVerify) + return buildSingleKeySigScript(keys[0], withVerify) } sortedKeys, err := prepareKeysForMultisigScript(keys) @@ -85,12 +98,12 @@ func BuildMultiSigScript( return nil, err } - return buildMultiSigScript(sortedKeys, threshold, withVerify) + return assembleMultiSigScript(sortedKeys, threshold, withVerify) } // Only holder of private key for given pubKey can spend after relative lock time // SCRIPT: OP_CHECKSIGVERIFY OP_CHECKSEQUENCEVERIFY -func BuildTimeLockScript( +func buildTimeLockScript( pubKey *btcec.PublicKey, lockTime uint16, ) ([]byte, error) { @@ -104,7 +117,7 @@ func BuildTimeLockScript( // Only holder of private key for given pubKey can spend // SCRIPT: OP_CHECKSIGVERIFY -func BuildSingleKeySigScript( +func buildSingleKeySigScript( pubKey *btcec.PublicKey, withVerify bool, ) ([]byte, error) { diff --git a/btcstaking/staking.go b/btcstaking/staking.go index 04fc4947a..22bd297f5 100644 --- a/btcstaking/staking.go +++ b/btcstaking/staking.go @@ -2,7 +2,6 @@ package btcstaking import ( "bytes" - "encoding/hex" "fmt" sdkmath "cosmossdk.io/math" @@ -17,63 +16,6 @@ import ( asig "github.com/babylonchain/babylon/crypto/schnorr-adaptor-signature" ) -const ( - - // Point with unknown discrete logarithm defined in: https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs - // using it as internal public key efectively disables taproot key spends - unspendableKeyPath = "0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0" -) - -var ( - unspendableKeyPathKey = unspendableKeyPathInternalPubKeyInternal(unspendableKeyPath) -) - -func unspendableKeyPathInternalPubKeyInternal(keyHex string) btcec.PublicKey { - keyBytes, err := hex.DecodeString(keyHex) - - if err != nil { - panic(fmt.Sprintf("unexpected error: %v", err)) - } - - // We are using btcec here, as key is 33 byte compressed format. - pubKey, err := btcec.ParsePubKey(keyBytes) - - if err != nil { - panic(fmt.Sprintf("unexpected error: %v", err)) - } - return *pubKey -} - -// StakingScriptData is a struct that holds data parsed from staking script -type StakingScriptData struct { - StakerKey *btcec.PublicKey - ValidatorKey *btcec.PublicKey - CovenantKey *btcec.PublicKey - StakingTime uint16 -} - -func NewStakingScriptData( - stakerKey, - validatorKey, - covenantKey *btcec.PublicKey, - stakingTime uint16) (*StakingScriptData, error) { - - if stakerKey == nil || validatorKey == nil || covenantKey == nil { - return nil, fmt.Errorf("staker, validator and covenant keys cannot be nil") - } - - return &StakingScriptData{ - StakerKey: stakerKey, - ValidatorKey: validatorKey, - CovenantKey: covenantKey, - StakingTime: stakingTime, - }, nil -} - -func UnspendableKeyPathInternalPubKey() btcec.PublicKey { - return unspendableKeyPathKey -} - // BuildSlashingTxFromOutpoint builds a valid slashing transaction by creating a new Bitcoin transaction that slashes a portion // of staked funds and directs them to a specified slashing address. The transaction also includes a change output sent back to // the specified change address. The slashing rate determines the proportion of staked funds to be slashed. @@ -752,19 +694,44 @@ func EncVerifyTransactionSigWithOutputData( return signature.EncVerify(pubKey, encKey, sigHash) } -// IsSlashingRateValid checks if the given slashing rate is between the valid range i.e., (0,1) with a precision of at most 2 decimal places. -func IsSlashingRateValid(slashingRate sdkmath.LegacyDec) bool { - // Check if the slashing rate is between 0 and 1 - if slashingRate.LTE(sdkmath.LegacyZeroDec()) || slashingRate.GTE(sdkmath.LegacyOneDec()) { - return false +// CreateBabylonWitness creates babylon compatible witness, as babylon scripts +// have witness with the same shape +// - first come signatures +// - then whole revealed script +// - then control block +func CreateBabylonWitness( + signatures [][]byte, + si *SpendInfo, +) (wire.TxWitness, error) { + numSignatures := len(signatures) + + if numSignatures == 0 { + return nil, fmt.Errorf("cannot build witness without signatures") + } + + if si == nil { + return nil, fmt.Errorf("cannot build witness without spend info") + } + + controlBlockBytes, err := si.ControlBlock.ToBytes() + + if err != nil { + return nil, err } - // Multiply by 100 to move the decimal places and check if precision is at most 2 decimal places - multipliedRate := slashingRate.Mul(sdkmath.LegacyNewDec(100)) + // witness stack has: + // all signatures + // whole revealed script + // control block + witnessStack := wire.TxWitness(make([][]byte, numSignatures+2)) + + for i, sig := range signatures { + sc := sig + witnessStack[i] = sc + } - // Truncate the rate to remove decimal places - truncatedRate := multipliedRate.TruncateDec() + witnessStack[numSignatures] = si.RevealedLeaf.Script + witnessStack[numSignatures+1] = controlBlockBytes - // Check if the truncated rate is equal to the original rate - return multipliedRate.Equal(truncatedRate) + return witnessStack, nil } diff --git a/btcstaking/staking_test.go b/btcstaking/staking_test.go index c3eacb655..d1b01111b 100644 --- a/btcstaking/staking_test.go +++ b/btcstaking/staking_test.go @@ -1,11 +1,13 @@ package btcstaking_test import ( - sdkmath "cosmossdk.io/math" + "fmt" "math" "math/rand" "testing" + sdkmath "cosmossdk.io/math" + "github.com/babylonchain/babylon/btcstaking" "github.com/babylonchain/babylon/testutil/datagen" "github.com/btcsuite/btcd/btcec/v2" @@ -16,7 +18,33 @@ import ( "github.com/stretchr/testify/require" ) -func genValidStakingScriptData(_ *testing.T, r *rand.Rand) *btcstaking.StakingScriptData { +// StakingScriptData is a struct that holds data parsed from staking script +type StakingScriptData struct { + StakerKey *btcec.PublicKey + ValidatorKey *btcec.PublicKey + CovenantKey *btcec.PublicKey + StakingTime uint16 +} + +func NewStakingScriptData( + stakerKey, + validatorKey, + covenantKey *btcec.PublicKey, + stakingTime uint16) (*StakingScriptData, error) { + + if stakerKey == nil || validatorKey == nil || covenantKey == nil { + return nil, fmt.Errorf("staker, validator and covenant keys cannot be nil") + } + + return &StakingScriptData{ + StakerKey: stakerKey, + ValidatorKey: validatorKey, + CovenantKey: covenantKey, + StakingTime: stakingTime, + }, nil +} + +func genValidStakingScriptData(_ *testing.T, r *rand.Rand) *StakingScriptData { stakerPrivKeyBytes := datagen.GenRandomByteArray(r, 32) validatorPrivKeyBytes := datagen.GenRandomByteArray(r, 32) covenantPrivKeyBytes := datagen.GenRandomByteArray(r, 32) @@ -26,7 +54,7 @@ func genValidStakingScriptData(_ *testing.T, r *rand.Rand) *btcstaking.StakingSc _, validatorPublicKey := btcec.PrivKeyFromBytes(validatorPrivKeyBytes) _, covenantPublicKey := btcec.PrivKeyFromBytes(covenantPrivKeyBytes) - sd, _ := btcstaking.NewStakingScriptData(stakerPublicKey, validatorPublicKey, covenantPublicKey, stakingTime) + sd, _ := NewStakingScriptData(stakerPublicKey, validatorPublicKey, covenantPublicKey, stakingTime) return sd } diff --git a/btcstaking/staking_utils.go b/btcstaking/staking_utils.go deleted file mode 100644 index 4c7ea0e12..000000000 --- a/btcstaking/staking_utils.go +++ /dev/null @@ -1,219 +0,0 @@ -package btcstaking - -import ( - "bytes" - "fmt" - "sort" - - "github.com/btcsuite/btcd/btcec/v2" - "github.com/btcsuite/btcd/btcec/v2/schnorr" - "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" -) - -// key sorting code copied from musig2 impl in btcd: https://github.com/btcsuite/btcd/blob/master/btcec/schnorr/musig2/keys.go -type sortableKeys []*btcec.PublicKey - -// Less reports whether the element with index i must sort before the element -// with index j. -func (s sortableKeys) Less(i, j int) bool { - // TODO(roasbeef): more efficient way to compare... - keyIBytes := schnorr.SerializePubKey(s[i]) - keyJBytes := schnorr.SerializePubKey(s[j]) - - return bytes.Compare(keyIBytes, keyJBytes) == -1 -} - -// Swap swaps the elements with indexes i and j. -func (s sortableKeys) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -// Len is the number of elements in the collection. -func (s sortableKeys) Len() int { - return len(s) -} - -// sortKeys takes a set of schnorr public keys and returns a new slice that is -// a copy of the keys sorted in lexicographical order bytes on the x-only -// pubkey serialization. -func sortKeys(keys []*btcec.PublicKey) []*btcec.PublicKey { - keySet := sortableKeys(keys) - if sort.IsSorted(keySet) { - return keys - } - - sort.Sort(keySet) - return keySet -} - -type SignatureInfo struct { - SignerPubKey *btcec.PublicKey - Signature []byte -} - -func NewSignatureInfo( - signerPubKey *btcec.PublicKey, - signature []byte, -) *SignatureInfo { - return &SignatureInfo{ - SignerPubKey: signerPubKey, - Signature: signature, - } -} - -type sortableSigInfo []*SignatureInfo - -// Less reports whether the element with index i must sort before the element -// with index j. -func (s sortableSigInfo) Less(i, j int) bool { - // TODO(roasbeef): more efficient way to compare... - keyIBytes := schnorr.SerializePubKey(s[i].SignerPubKey) - keyJBytes := schnorr.SerializePubKey(s[j].SignerPubKey) - - return bytes.Compare(keyIBytes, keyJBytes) == 1 -} - -// Swap swaps the elements with indexes i and j. -func (s sortableSigInfo) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -// Len is the number of elements in the collection. -func (s sortableSigInfo) Len() int { - return len(s) -} - -// Helper function to sort all signatures in reverse lexicographical order of signing public keys -// this way signatures are ready to be used in multisig witness with corresponding public keys -func SortSignatureInfo(infos []*SignatureInfo) []*SignatureInfo { - keySet := sortableSigInfo(infos) - if sort.IsSorted(keySet) { - return infos - } - - sort.Sort(keySet) - return keySet -} - -func SpendInfoFromRevealedScript( - revealedScript []byte, - internalKey *btcec.PublicKey, - tree *txscript.IndexedTapScriptTree) (*SpendInfo, error) { - - revealedLeaf := txscript.NewBaseTapLeaf(revealedScript) - leafHash := revealedLeaf.TapHash() - - scriptIdx, ok := tree.LeafProofIndex[leafHash] - - if !ok { - return nil, fmt.Errorf("script not found in script tree") - } - - merkleProof := tree.LeafMerkleProofs[scriptIdx] - - return &SpendInfo{ - ControlBlock: merkleProof.ToControlBlock(internalKey), - RevealedLeaf: revealedLeaf, - }, nil -} - -func NewTaprootTreeFromScripts( - scripts [][]byte, -) *txscript.IndexedTapScriptTree { - var tapLeafs []txscript.TapLeaf - for _, script := range scripts { - scr := script - tapLeafs = append(tapLeafs, txscript.NewBaseTapLeaf(scr)) - } - return txscript.AssembleTaprootScriptTree(tapLeafs...) -} - -func DeriveTaprootAddress( - tapScriptTree *txscript.IndexedTapScriptTree, - internalPubKey *btcec.PublicKey, - net *chaincfg.Params) (*btcutil.AddressTaproot, error) { - - tapScriptRootHash := tapScriptTree.RootNode.TapHash() - - outputKey := txscript.ComputeTaprootOutputKey( - internalPubKey, tapScriptRootHash[:], - ) - - address, err := btcutil.NewAddressTaproot( - schnorr.SerializePubKey(outputKey), net) - - if err != nil { - return nil, fmt.Errorf("error encoding Taproot address: %v", err) - } - - return address, nil -} - -func DeriveTaprootPkScript( - tapScriptTree *txscript.IndexedTapScriptTree, - internalPubKey *btcec.PublicKey, - net *chaincfg.Params, -) ([]byte, error) { - taprootAddress, err := DeriveTaprootAddress( - tapScriptTree, - &unspendableKeyPathKey, - net, - ) - - if err != nil { - return nil, err - } - - taprootPkScript, err := txscript.PayToAddrScript(taprootAddress) - - if err != nil { - return nil, err - } - - return taprootPkScript, nil -} - -// CreateBabylonWitness creates babylon compatible witness, as babylon scripts -// has witness with the same shape -// - first come signatures -// - then whole revealed script -// - then control block -func CreateBabylonWitness( - signatures [][]byte, - si *SpendInfo, -) (wire.TxWitness, error) { - numSignatures := len(signatures) - - if numSignatures == 0 { - return nil, fmt.Errorf("cannot build witness without signatures") - } - - if si == nil { - return nil, fmt.Errorf("cannot build witness without spend info") - } - - controlBlockBytes, err := si.ControlBlock.ToBytes() - - if err != nil { - return nil, err - } - - // witness stack has: - // all signatures - // whole revealed script - // control block - witnessStack := wire.TxWitness(make([][]byte, numSignatures+2)) - - for i, sig := range signatures { - sc := sig - witnessStack[i] = sc - } - - witnessStack[numSignatures] = si.RevealedLeaf.Script - witnessStack[numSignatures+1] = controlBlockBytes - - return witnessStack, nil -} diff --git a/btcstaking/btc_staking.go b/btcstaking/types.go similarity index 63% rename from btcstaking/btc_staking.go rename to btcstaking/types.go index 40402c5f1..5bf8d9c38 100644 --- a/btcstaking/btc_staking.go +++ b/btcstaking/types.go @@ -1,9 +1,14 @@ package btcstaking import ( + "bytes" + "encoding/hex" "fmt" + "sort" + sdkmath "cosmossdk.io/math" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -11,6 +16,92 @@ import ( "github.com/btcsuite/btcd/wire" ) +const ( + // Point with unknown discrete logarithm defined in: https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs + // using it as internal public key efectively disables taproot key spends + unspendableKeyPath = "0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0" +) + +var ( + unspendableKeyPathKey = unspendableKeyPathInternalPubKeyInternal(unspendableKeyPath) +) + +func unspendableKeyPathInternalPubKeyInternal(keyHex string) btcec.PublicKey { + keyBytes, err := hex.DecodeString(keyHex) + + if err != nil { + panic(fmt.Sprintf("unexpected error: %v", err)) + } + + // We are using btcec here, as key is 33 byte compressed format. + pubKey, err := btcec.ParsePubKey(keyBytes) + + if err != nil { + panic(fmt.Sprintf("unexpected error: %v", err)) + } + return *pubKey +} + +func unspendableKeyPathInternalPubKey() btcec.PublicKey { + return unspendableKeyPathKey +} + +func NewTaprootTreeFromScripts( + scripts [][]byte, +) *txscript.IndexedTapScriptTree { + var tapLeafs []txscript.TapLeaf + for _, script := range scripts { + scr := script + tapLeafs = append(tapLeafs, txscript.NewBaseTapLeaf(scr)) + } + return txscript.AssembleTaprootScriptTree(tapLeafs...) +} + +func DeriveTaprootAddress( + tapScriptTree *txscript.IndexedTapScriptTree, + internalPubKey *btcec.PublicKey, + net *chaincfg.Params) (*btcutil.AddressTaproot, error) { + + tapScriptRootHash := tapScriptTree.RootNode.TapHash() + + outputKey := txscript.ComputeTaprootOutputKey( + internalPubKey, tapScriptRootHash[:], + ) + + address, err := btcutil.NewAddressTaproot( + schnorr.SerializePubKey(outputKey), net) + + if err != nil { + return nil, fmt.Errorf("error encoding Taproot address: %v", err) + } + + return address, nil +} + +func DeriveTaprootPkScript( + tapScriptTree *txscript.IndexedTapScriptTree, + internalPubKey *btcec.PublicKey, + net *chaincfg.Params, +) ([]byte, error) { + taprootAddress, err := DeriveTaprootAddress( + tapScriptTree, + &unspendableKeyPathKey, + net, + ) + + if err != nil { + return nil, err + } + + taprootPkScript, err := txscript.PayToAddrScript(taprootAddress) + + if err != nil { + return nil, err + } + + return taprootPkScript, nil +} + type taprootScriptHolder struct { internalPubKey *btcec.PublicKey scriptTree *txscript.IndexedTapScriptTree @@ -83,6 +174,33 @@ func (t *taprootScriptHolder) taprootPkScript(net *chaincfg.Params) ([]byte, err ) } +type SignatureInfo struct { + SignerPubKey *btcec.PublicKey + Signature []byte +} + +func NewSignatureInfo( + signerPubKey *btcec.PublicKey, + signature []byte, +) *SignatureInfo { + return &SignatureInfo{ + SignerPubKey: signerPubKey, + Signature: signature, + } +} + +// Helper function to sort all signatures in reverse lexicographical order of signing public keys +// this way signatures are ready to be used in multisig witness with corresponding public keys +func SortSignatureInfo(infos []*SignatureInfo) []*SignatureInfo { + sort.SliceStable(infos, func(i, j int) bool { + keyIBytes := schnorr.SerializePubKey(infos[i].SignerPubKey) + keyJBytes := schnorr.SerializePubKey(infos[j].SignerPubKey) + return bytes.Compare(keyIBytes, keyJBytes) == 1 + }) + + return infos +} + // Package responsible for different kinds of btc scripts used by babylon // Staking script has 3 spending paths: // 1. Staker can spend after relative time lock - staking @@ -105,6 +223,28 @@ type SpendInfo struct { RevealedLeaf txscript.TapLeaf } +func SpendInfoFromRevealedScript( + revealedScript []byte, + internalKey *btcec.PublicKey, + tree *txscript.IndexedTapScriptTree) (*SpendInfo, error) { + + revealedLeaf := txscript.NewBaseTapLeaf(revealedScript) + leafHash := revealedLeaf.TapHash() + + scriptIdx, ok := tree.LeafProofIndex[leafHash] + + if !ok { + return nil, fmt.Errorf("script not found in script tree") + } + + merkleProof := tree.LeafMerkleProofs[scriptIdx] + + return &SpendInfo{ + ControlBlock: merkleProof.ToControlBlock(internalKey), + RevealedLeaf: revealedLeaf, + }, nil +} + func aggregateScripts(scripts ...[]byte) []byte { if len(scripts) == 0 { return []byte{} @@ -118,7 +258,7 @@ func aggregateScripts(scripts ...[]byte) []byte { return finalScript } -// babylonScriptPaths is and aggregate of all possible babylon script paths +// babylonScriptPaths contains all possible babylon script paths // not every babylon output will contain all of those paths type babylonScriptPaths struct { timeLockPathScript []byte @@ -137,13 +277,13 @@ func newBabylonScriptPaths( return nil, fmt.Errorf("staker key is nil") } - timeLockPathScript, err := BuildTimeLockScript(stakerKey, lockTime) + timeLockPathScript, err := buildTimeLockScript(stakerKey, lockTime) if err != nil { return nil, err } - covenantMultisigScript, err := BuildMultiSigScript( + covenantMultisigScript, err := buildMultiSigScript( covenantKeys, covenantThreshold, // covenant multisig is always last in script so we do not run verify and leave @@ -156,13 +296,13 @@ func newBabylonScriptPaths( return nil, err } - stakerSigScript, err := BuildSingleKeySigScript(stakerKey, true) + stakerSigScript, err := buildSingleKeySigScript(stakerKey, true) if err != nil { return nil, err } - validatorSigScript, err := BuildMultiSigScript( + validatorSigScript, err := buildMultiSigScript( validatorKeys, // we always require only one validator to sign 1, @@ -201,7 +341,7 @@ func BuildStakingInfo( stakingAmount btcutil.Amount, net *chaincfg.Params, ) (*StakingInfo, error) { - unspendableKeyPathKey := UnspendableKeyPathInternalPubKey() + unspendableKeyPathKey := unspendableKeyPathInternalPubKey() babylonScripts, err := newBabylonScriptPaths( stakerKey, @@ -281,7 +421,7 @@ func BuildUnbondingInfo( unbondingAmount btcutil.Amount, net *chaincfg.Params, ) (*UnbondingInfo, error) { - unspendableKeyPathKey := UnspendableKeyPathInternalPubKey() + unspendableKeyPathKey := unspendableKeyPathInternalPubKey() babylonScripts, err := newBabylonScriptPaths( stakerKey, @@ -334,3 +474,20 @@ func (i *UnbondingInfo) TimeLockPathSpendInfo() (*SpendInfo, error) { func (i *UnbondingInfo) SlashingPathSpendInfo() (*SpendInfo, error) { return i.scriptHolder.scriptSpendInfoByName(i.slashingPathLeafHash) } + +// IsSlashingRateValid checks if the given slashing rate is between the valid range i.e., (0,1) with a precision of at most 2 decimal places. +func IsSlashingRateValid(slashingRate sdkmath.LegacyDec) bool { + // Check if the slashing rate is between 0 and 1 + if slashingRate.LTE(sdkmath.LegacyZeroDec()) || slashingRate.GTE(sdkmath.LegacyOneDec()) { + return false + } + + // Multiply by 100 to move the decimal places and check if precision is at most 2 decimal places + multipliedRate := slashingRate.Mul(sdkmath.LegacyNewDec(100)) + + // Truncate the rate to remove decimal places + truncatedRate := multipliedRate.TruncateDec() + + // Check if the truncated rate is equal to the original rate + return multipliedRate.Equal(truncatedRate) +} diff --git a/go.mod b/go.mod index 62bbabfd8..5dd4cfceb 100644 --- a/go.mod +++ b/go.mod @@ -120,7 +120,7 @@ require ( github.com/tendermint/go-amino v0.16.0 // indirect github.com/zondax/hid v0.9.2 // indirect go.etcd.io/bbolt v1.3.7 // indirect - golang.org/x/crypto v0.14.0 // indirect + golang.org/x/crypto v0.14.0 golang.org/x/sys v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect diff --git a/x/btcstaking/types/types.go b/x/btcstaking/types/types.go index fac4aab24..6f3138b58 100644 --- a/x/btcstaking/types/types.go +++ b/x/btcstaking/types/types.go @@ -6,7 +6,6 @@ import ( "fmt" "math" - "github.com/babylonchain/babylon/btcstaking" bbn "github.com/babylonchain/babylon/types" "github.com/btcsuite/btcd/wire" ) @@ -17,14 +16,6 @@ type PublicKeyInfo struct { CovenantKey *bbn.BIP340PubKey } -func KeyDataFromScript(scriptData *btcstaking.StakingScriptData) *PublicKeyInfo { - return &PublicKeyInfo{ - StakerKey: bbn.NewBIP340PubKeyFromBTCPK(scriptData.StakerKey), - ValidatorKey: bbn.NewBIP340PubKeyFromBTCPK(scriptData.ValidatorKey), - CovenantKey: bbn.NewBIP340PubKeyFromBTCPK(scriptData.CovenantKey), - } -} - func ParseBtcTx(txBytes []byte) (*wire.MsgTx, error) { var msgTx wire.MsgTx rbuf := bytes.NewReader(txBytes)