mirror of
https://github.com/aljazceru/ark.git
synced 2026-02-22 19:34:20 +01:00
Change representation of taproot trees & Internal fixes (#384)
* migrate descriptors --> tapscripts * fix covenantless * dynamic boarding exit delay * remove duplicates in tree and bitcointree * agnostic signatures validation * revert GetInfo change * renaming VtxoScript var * Agnostic script server (#6) * Hotfix: Prevent ZMQ-based bitcoin wallet to panic (#383) * Hotfix bct embedded wallet w/ ZMQ * Fixes * Rename vtxo is_oor to is_pending (#385) * Rename vtxo is_oor > is_pending * Clean swaggers * Revert changes to client and sdk * descriptor in oneof * support CHECKSIG_ADD in MultisigClosure * use right witness size in OOR tx fee estimation * Revert changes --------- Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com>
This commit is contained in:
@@ -89,7 +89,7 @@ func (o Outpoint) Equals(other Outpoint) bool {
|
||||
|
||||
type Input struct {
|
||||
Outpoint
|
||||
Descriptor string
|
||||
Tapscripts []string
|
||||
}
|
||||
|
||||
type AsyncPaymentInput struct {
|
||||
@@ -129,9 +129,9 @@ func (v Vtxo) Address(asp *secp256k1.PublicKey, net common.Network) (string, err
|
||||
return a.Encode()
|
||||
}
|
||||
|
||||
type DescriptorVtxo struct {
|
||||
type TapscriptsVtxo struct {
|
||||
Vtxo
|
||||
Descriptor string
|
||||
Tapscripts []string
|
||||
}
|
||||
|
||||
type Output struct {
|
||||
|
||||
@@ -144,7 +144,11 @@ func toProtoInput(i client.Input) *arkv1.Input {
|
||||
Txid: i.Txid,
|
||||
Vout: i.VOut,
|
||||
},
|
||||
Descriptor_: i.Descriptor,
|
||||
TaprootTree: &arkv1.Input_Tapscripts{
|
||||
Tapscripts: &arkv1.Tapscripts{
|
||||
Scripts: i.Tapscripts,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,9 @@ func (a *restClient) RegisterInputsForNextRound(
|
||||
Txid: i.Txid,
|
||||
Vout: int64(i.VOut),
|
||||
},
|
||||
Descriptor: i.Descriptor,
|
||||
Tapscripts: &models.V1Tapscripts{
|
||||
Scripts: i.Tapscripts,
|
||||
},
|
||||
})
|
||||
}
|
||||
body := &models.V1RegisterInputsForNextRoundRequest{
|
||||
@@ -402,7 +404,9 @@ func (a *restClient) CreatePayment(
|
||||
Txid: i.Input.Txid,
|
||||
Vout: int64(i.VOut),
|
||||
},
|
||||
Descriptor: i.Input.Descriptor,
|
||||
Tapscripts: &models.V1Tapscripts{
|
||||
Scripts: i.Input.Tapscripts,
|
||||
},
|
||||
},
|
||||
ForfeitLeafHash: i.ForfeitLeafHash.String(),
|
||||
})
|
||||
|
||||
@@ -8,6 +8,7 @@ package models
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
@@ -22,15 +23,76 @@ type V1GetBoardingAddressResponse struct {
|
||||
|
||||
// descriptor
|
||||
Descriptor string `json:"descriptor,omitempty"`
|
||||
|
||||
// tapscripts
|
||||
Tapscripts *V1Tapscripts `json:"tapscripts,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 get boarding address response
|
||||
func (m *V1GetBoardingAddressResponse) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateTapscripts(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this v1 get boarding address response based on context it is used
|
||||
func (m *V1GetBoardingAddressResponse) validateTapscripts(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Tapscripts) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Tapscripts != nil {
|
||||
if err := m.Tapscripts.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("tapscripts")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("tapscripts")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this v1 get boarding address response based on the context it is used
|
||||
func (m *V1GetBoardingAddressResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateTapscripts(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1GetBoardingAddressResponse) contextValidateTapscripts(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.Tapscripts != nil {
|
||||
|
||||
if swag.IsZero(m.Tapscripts) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.Tapscripts.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("tapscripts")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("tapscripts")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ type V1Input struct {
|
||||
|
||||
// outpoint
|
||||
Outpoint *V1Outpoint `json:"outpoint,omitempty"`
|
||||
|
||||
// tapscripts
|
||||
Tapscripts *V1Tapscripts `json:"tapscripts,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 input
|
||||
@@ -33,6 +36,10 @@ func (m *V1Input) Validate(formats strfmt.Registry) error {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateTapscripts(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
@@ -58,6 +65,25 @@ func (m *V1Input) validateOutpoint(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1Input) validateTapscripts(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Tapscripts) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Tapscripts != nil {
|
||||
if err := m.Tapscripts.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("tapscripts")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("tapscripts")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this v1 input based on the context it is used
|
||||
func (m *V1Input) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
@@ -66,6 +92,10 @@ func (m *V1Input) ContextValidate(ctx context.Context, formats strfmt.Registry)
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateTapscripts(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
@@ -93,6 +123,27 @@ func (m *V1Input) contextValidateOutpoint(ctx context.Context, formats strfmt.Re
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *V1Input) contextValidateTapscripts(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.Tapscripts != nil {
|
||||
|
||||
if swag.IsZero(m.Tapscripts) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.Tapscripts.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("tapscripts")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("tapscripts")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *V1Input) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// V1OwnershipProof This message is used to prove to the ASP that the user controls the vtxo without revealing the whole VTXO descriptor.
|
||||
// V1OwnershipProof This message is used to prove to the ASP that the user controls the vtxo without revealing the whole VTXO taproot tree.
|
||||
//
|
||||
// swagger:model v1OwnershipProof
|
||||
type V1OwnershipProof struct {
|
||||
|
||||
50
pkg/client-sdk/client/rest/service/models/v1_tapscripts.go
Normal file
50
pkg/client-sdk/client/rest/service/models/v1_tapscripts.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// V1Tapscripts v1 tapscripts
|
||||
//
|
||||
// swagger:model v1Tapscripts
|
||||
type V1Tapscripts struct {
|
||||
|
||||
// scripts
|
||||
Scripts []string `json:"scripts"`
|
||||
}
|
||||
|
||||
// Validate validates this v1 tapscripts
|
||||
func (m *V1Tapscripts) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this v1 tapscripts based on context it is used
|
||||
func (m *V1Tapscripts) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *V1Tapscripts) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *V1Tapscripts) UnmarshalBinary(b []byte) error {
|
||||
var res V1Tapscripts
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vulpemventures/go-elements/address"
|
||||
@@ -518,7 +517,7 @@ func (a *covenantArkClient) CollaborativeRedeem(
|
||||
},
|
||||
}
|
||||
|
||||
vtxos := make([]client.DescriptorVtxo, 0)
|
||||
vtxos := make([]client.TapscriptsVtxo, 0)
|
||||
spendableVtxos, err := a.getVtxos(ctx, false, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -532,9 +531,9 @@ func (a *covenantArkClient) CollaborativeRedeem(
|
||||
}
|
||||
|
||||
if vtxoAddr == offchainAddr.Address {
|
||||
vtxos = append(vtxos, client.DescriptorVtxo{
|
||||
vtxos = append(vtxos, client.TapscriptsVtxo{
|
||||
Vtxo: v,
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -572,7 +571,7 @@ func (a *covenantArkClient) CollaborativeRedeem(
|
||||
Txid: coin.Txid,
|
||||
VOut: coin.VOut,
|
||||
},
|
||||
Descriptor: coin.Descriptor,
|
||||
Tapscripts: coin.Tapscripts,
|
||||
})
|
||||
}
|
||||
for _, coin := range selectedBoardingUtxos {
|
||||
@@ -581,7 +580,7 @@ func (a *covenantArkClient) CollaborativeRedeem(
|
||||
Txid: coin.Txid,
|
||||
VOut: coin.VOut,
|
||||
},
|
||||
Descriptor: coin.Descriptor,
|
||||
Tapscripts: coin.Tapscripts,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -661,7 +660,7 @@ func (a *covenantArkClient) getAllBoardingUtxos(ctx context.Context) ([]types.Ut
|
||||
VOut: uint32(i),
|
||||
Amount: vout.Amount,
|
||||
CreatedAt: createdAt,
|
||||
Descriptor: addr.Descriptor,
|
||||
Tapscripts: addr.Tapscripts,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -680,17 +679,14 @@ func (a *covenantArkClient) getClaimableBoardingUtxos(ctx context.Context, opts
|
||||
claimable := make([]types.Utxo, 0)
|
||||
|
||||
for _, addr := range boardingAddrs {
|
||||
boardingScript, err := tree.ParseVtxoScript(addr.Descriptor)
|
||||
boardingScript, err := tree.ParseVtxoScript(addr.Tapscripts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var boardingTimeout uint
|
||||
|
||||
if defaultVtxo, ok := boardingScript.(*tree.DefaultVtxoScript); ok {
|
||||
boardingTimeout = defaultVtxo.ExitDelay
|
||||
} else {
|
||||
return nil, fmt.Errorf("unsupported boarding descriptor: %s", addr.Descriptor)
|
||||
boardingTimeout, err := boardingScript.SmallestExitDelay()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
boardingUtxos, err := a.explorer.GetUtxos(addr.Address)
|
||||
@@ -719,7 +715,7 @@ func (a *covenantArkClient) getClaimableBoardingUtxos(ctx context.Context, opts
|
||||
}
|
||||
}
|
||||
|
||||
u := utxo.ToUtxo(boardingTimeout, addr.Descriptor)
|
||||
u := utxo.ToUtxo(boardingTimeout, addr.Tapscripts)
|
||||
if u.SpendableAt.Before(now) {
|
||||
continue
|
||||
}
|
||||
@@ -929,7 +925,7 @@ func (a *covenantArkClient) sendOffchain(
|
||||
return "", fmt.Errorf("no offchain addresses found")
|
||||
}
|
||||
|
||||
vtxos := make([]client.DescriptorVtxo, 0)
|
||||
vtxos := make([]client.TapscriptsVtxo, 0)
|
||||
|
||||
spendableVtxos, err := a.getVtxos(ctx, withExpiryCoinselect, nil)
|
||||
if err != nil {
|
||||
@@ -944,9 +940,9 @@ func (a *covenantArkClient) sendOffchain(
|
||||
}
|
||||
|
||||
if vtxoAddr == offchainAddr.Address {
|
||||
vtxos = append(vtxos, client.DescriptorVtxo{
|
||||
vtxos = append(vtxos, client.TapscriptsVtxo{
|
||||
Vtxo: v,
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -958,7 +954,7 @@ func (a *covenantArkClient) sendOffchain(
|
||||
}
|
||||
|
||||
var selectedBoardingCoins []types.Utxo
|
||||
var selectedCoins []client.DescriptorVtxo
|
||||
var selectedCoins []client.TapscriptsVtxo
|
||||
var changeAmount uint64
|
||||
|
||||
// if no receivers, self send all selected coins
|
||||
@@ -1009,7 +1005,7 @@ func (a *covenantArkClient) sendOffchain(
|
||||
Txid: coin.Txid,
|
||||
VOut: coin.VOut,
|
||||
},
|
||||
Descriptor: coin.Descriptor,
|
||||
Tapscripts: coin.Tapscripts,
|
||||
})
|
||||
}
|
||||
for _, coin := range selectedBoardingCoins {
|
||||
@@ -1018,7 +1014,7 @@ func (a *covenantArkClient) sendOffchain(
|
||||
Txid: coin.Txid,
|
||||
VOut: coin.VOut,
|
||||
},
|
||||
Descriptor: coin.Descriptor,
|
||||
Tapscripts: coin.Tapscripts,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1057,19 +1053,33 @@ func (a *covenantArkClient) addInputs(
|
||||
return err
|
||||
}
|
||||
|
||||
vtxoScript, err := tree.ParseVtxoScript(offchain.Descriptor)
|
||||
vtxoScript, err := tree.ParseVtxoScript(offchain.Tapscripts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var userPubkey, aspPubkey *secp256k1.PublicKey
|
||||
forfeitClosure := vtxoScript.ForfeitClosures()[0]
|
||||
|
||||
switch s := vtxoScript.(type) {
|
||||
case *tree.DefaultVtxoScript:
|
||||
userPubkey = s.Owner
|
||||
aspPubkey = s.Asp
|
||||
default:
|
||||
return fmt.Errorf("unsupported vtxo script: %T", s)
|
||||
forfeitScript, err := forfeitClosure.Script()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
forfeitLeaf := taproot.NewBaseTapElementsLeaf(forfeitScript)
|
||||
|
||||
_, taprootTree, err := vtxoScript.TapTree()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
leafProof, err := taprootTree.GetTaprootMerkleProof(forfeitLeaf.TapHash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
controlBlock, err := taproot.ParseControlBlock(leafProof.Script)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, utxo := range utxos {
|
||||
@@ -1088,37 +1098,6 @@ func (a *covenantArkClient) addInputs(
|
||||
return err
|
||||
}
|
||||
|
||||
vtxoScript := &tree.DefaultVtxoScript{
|
||||
Owner: userPubkey,
|
||||
Asp: aspPubkey,
|
||||
ExitDelay: utxo.Delay,
|
||||
}
|
||||
|
||||
forfeitClosure := &tree.MultisigClosure{
|
||||
Pubkey: userPubkey,
|
||||
AspPubkey: aspPubkey,
|
||||
}
|
||||
|
||||
forfeitLeaf, err := forfeitClosure.Leaf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, taprootTree, err := vtxoScript.TapTree()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
leafProof, err := taprootTree.GetTaprootMerkleProof(forfeitLeaf.TapHash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
controlBlock, err := taproot.ParseControlBlock(leafProof.Script)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
inputIndex := len(updater.Pset.Inputs) - 1
|
||||
|
||||
if err := updater.AddInTapLeafScript(
|
||||
@@ -1138,7 +1117,7 @@ func (a *covenantArkClient) addInputs(
|
||||
func (a *covenantArkClient) handleRoundStream(
|
||||
ctx context.Context,
|
||||
paymentID string,
|
||||
vtxosToSign []client.DescriptorVtxo,
|
||||
vtxosToSign []client.TapscriptsVtxo,
|
||||
boardingUtxos []types.Utxo,
|
||||
receivers []client.Output,
|
||||
) (string, error) {
|
||||
@@ -1202,7 +1181,7 @@ func (a *covenantArkClient) handleRoundStream(
|
||||
func (a *covenantArkClient) handleRoundFinalization(
|
||||
ctx context.Context,
|
||||
event client.RoundFinalizationEvent,
|
||||
vtxos []client.DescriptorVtxo,
|
||||
vtxos []client.TapscriptsVtxo,
|
||||
boardingUtxos []types.Utxo,
|
||||
receivers []client.Output,
|
||||
) (signedForfeits []string, signedRoundTx string, err error) {
|
||||
@@ -1233,28 +1212,20 @@ func (a *covenantArkClient) handleRoundFinalization(
|
||||
}
|
||||
|
||||
for _, boardingUtxo := range boardingUtxos {
|
||||
boardingVtxoScript, err := tree.ParseVtxoScript(boardingUtxo.Descriptor)
|
||||
boardingVtxoScript, err := tree.ParseVtxoScript(boardingUtxo.Tapscripts)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var forfeitClosure tree.Closure
|
||||
forfeitClosure := boardingVtxoScript.ForfeitClosures()[0]
|
||||
|
||||
switch s := boardingVtxoScript.(type) {
|
||||
case *tree.DefaultVtxoScript:
|
||||
forfeitClosure = &tree.MultisigClosure{
|
||||
Pubkey: s.Owner,
|
||||
AspPubkey: a.AspPubkey,
|
||||
}
|
||||
default:
|
||||
return nil, "", fmt.Errorf("unsupported boarding descriptor: %s", boardingUtxo.Descriptor)
|
||||
}
|
||||
|
||||
forfeitLeaf, err := forfeitClosure.Leaf()
|
||||
forfeitScript, err := forfeitClosure.Script()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
forfeitLeaf := taproot.NewBaseTapElementsLeaf(forfeitScript)
|
||||
|
||||
_, taprootTree, err := boardingVtxoScript.TapTree()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
@@ -1430,7 +1401,7 @@ func (a *covenantArkClient) validateOffChainReceiver(
|
||||
|
||||
func (a *covenantArkClient) createAndSignForfeits(
|
||||
ctx context.Context,
|
||||
vtxosToSign []client.DescriptorVtxo,
|
||||
vtxosToSign []client.TapscriptsVtxo,
|
||||
connectors []string,
|
||||
feeRate chainfee.SatPerKVByte,
|
||||
) ([]string, error) {
|
||||
@@ -1452,7 +1423,7 @@ func (a *covenantArkClient) createAndSignForfeits(
|
||||
}
|
||||
|
||||
for _, vtxo := range vtxosToSign {
|
||||
vtxoScript, err := tree.ParseVtxoScript(vtxo.Descriptor)
|
||||
vtxoScript, err := tree.ParseVtxoScript(vtxo.Tapscripts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1472,25 +1443,15 @@ func (a *covenantArkClient) createAndSignForfeits(
|
||||
TxIndex: vtxo.VOut,
|
||||
}
|
||||
|
||||
var forfeitClosure tree.Closure
|
||||
var witnessSize int
|
||||
forfeitClosure := vtxoScript.ForfeitClosures()[0]
|
||||
|
||||
switch s := vtxoScript.(type) {
|
||||
case *tree.DefaultVtxoScript:
|
||||
forfeitClosure = &tree.MultisigClosure{
|
||||
Pubkey: s.Owner,
|
||||
AspPubkey: a.AspPubkey,
|
||||
}
|
||||
witnessSize = 64 * 2
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported vtxo script: %T", s)
|
||||
}
|
||||
|
||||
forfeitLeaf, err := forfeitClosure.Leaf()
|
||||
forfeitScript, err := forfeitClosure.Script()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
forfeitLeaf := taproot.NewBaseTapElementsLeaf(forfeitScript)
|
||||
|
||||
leafProof, err := vtxoTapTree.GetTaprootMerkleProof(forfeitLeaf.TapHash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1512,7 +1473,7 @@ func (a *covenantArkClient) createAndSignForfeits(
|
||||
RevealedScript: leafProof.Script,
|
||||
ControlBlock: &ctrlBlock.ControlBlock,
|
||||
},
|
||||
witnessSize,
|
||||
forfeitClosure.WitnessSize(),
|
||||
txscript.WitnessV0PubKeyHashTy,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -1567,19 +1528,14 @@ func (a *covenantArkClient) coinSelectOnchain(
|
||||
|
||||
fetchedUtxos := make([]types.Utxo, 0)
|
||||
for _, addr := range boardingAddrs {
|
||||
boardingDescriptor := addr.Descriptor
|
||||
|
||||
boardingScript, err := tree.ParseVtxoScript(boardingDescriptor)
|
||||
boardingScript, err := tree.ParseVtxoScript(addr.Tapscripts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var boardingTimeout uint
|
||||
|
||||
if defaultVtxo, ok := boardingScript.(*tree.DefaultVtxoScript); ok {
|
||||
boardingTimeout = defaultVtxo.ExitDelay
|
||||
} else {
|
||||
return nil, 0, fmt.Errorf("unsupported boarding descriptor: %s", boardingDescriptor)
|
||||
boardingTimeout, err := boardingScript.SmallestExitDelay()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
utxos, err := a.explorer.GetUtxos(addr.Address)
|
||||
@@ -1588,7 +1544,7 @@ func (a *covenantArkClient) coinSelectOnchain(
|
||||
}
|
||||
|
||||
for _, utxo := range utxos {
|
||||
u := utxo.ToUtxo(boardingTimeout, addr.Descriptor)
|
||||
u := utxo.ToUtxo(boardingTimeout, addr.Tapscripts)
|
||||
if u.SpendableAt.Before(now) {
|
||||
fetchedUtxos = append(fetchedUtxos, u)
|
||||
}
|
||||
@@ -1624,7 +1580,7 @@ func (a *covenantArkClient) coinSelectOnchain(
|
||||
}
|
||||
|
||||
for _, utxo := range utxos {
|
||||
u := utxo.ToUtxo(uint(a.UnilateralExitDelay), addr.Descriptor)
|
||||
u := utxo.ToUtxo(uint(a.UnilateralExitDelay), addr.Tapscripts)
|
||||
if u.SpendableAt.Before(now) {
|
||||
fetchedUtxos = append(fetchedUtxos, u)
|
||||
}
|
||||
|
||||
@@ -857,7 +857,7 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
|
||||
},
|
||||
}
|
||||
|
||||
vtxos := make([]client.DescriptorVtxo, 0)
|
||||
vtxos := make([]client.TapscriptsVtxo, 0)
|
||||
spendableVtxos, err := a.getVtxos(ctx, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -871,9 +871,9 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
|
||||
}
|
||||
|
||||
if vtxoAddr == offchainAddr.Address {
|
||||
vtxos = append(vtxos, client.DescriptorVtxo{
|
||||
vtxos = append(vtxos, client.TapscriptsVtxo{
|
||||
Vtxo: v,
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -911,7 +911,7 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
|
||||
Txid: coin.Txid,
|
||||
VOut: coin.VOut,
|
||||
},
|
||||
Descriptor: coin.Descriptor,
|
||||
Tapscripts: coin.Tapscripts,
|
||||
})
|
||||
}
|
||||
for _, coin := range selectedBoardingCoins {
|
||||
@@ -920,7 +920,7 @@ func (a *covenantlessArkClient) CollaborativeRedeem(
|
||||
Txid: coin.Txid,
|
||||
VOut: coin.VOut,
|
||||
},
|
||||
Descriptor: coin.Descriptor,
|
||||
Tapscripts: coin.Tapscripts,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1004,7 +1004,7 @@ func (a *covenantlessArkClient) SendAsync(
|
||||
sumOfReceivers += receiver.Amount()
|
||||
}
|
||||
|
||||
vtxos := make([]client.DescriptorVtxo, 0)
|
||||
vtxos := make([]client.TapscriptsVtxo, 0)
|
||||
opts := &CoinSelectOptions{
|
||||
WithExpirySorting: withExpiryCoinselect,
|
||||
}
|
||||
@@ -1021,9 +1021,9 @@ func (a *covenantlessArkClient) SendAsync(
|
||||
}
|
||||
|
||||
if vtxoAddr == offchainAddr.Address {
|
||||
vtxos = append(vtxos, client.DescriptorVtxo{
|
||||
vtxos = append(vtxos, client.TapscriptsVtxo{
|
||||
Vtxo: v,
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1048,35 +1048,27 @@ func (a *covenantlessArkClient) SendAsync(
|
||||
inputs := make([]client.AsyncPaymentInput, 0, len(selectedCoins))
|
||||
|
||||
for _, coin := range selectedCoins {
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(coin.Descriptor)
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(coin.Tapscripts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var forfeitClosure bitcointree.Closure
|
||||
forfeitClosure := vtxoScript.ForfeitClosures()[0]
|
||||
|
||||
switch s := vtxoScript.(type) {
|
||||
case *bitcointree.DefaultVtxoScript:
|
||||
forfeitClosure = &bitcointree.MultisigClosure{
|
||||
Pubkey: s.Owner,
|
||||
AspPubkey: s.Asp,
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported vtxo script: %T", s)
|
||||
}
|
||||
|
||||
forfeitLeaf, err := forfeitClosure.Leaf()
|
||||
forfeitScript, err := forfeitClosure.Script()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
forfeitLeaf := txscript.NewBaseTapLeaf(forfeitScript)
|
||||
|
||||
inputs = append(inputs, client.AsyncPaymentInput{
|
||||
Input: client.Input{
|
||||
Outpoint: client.Outpoint{
|
||||
Txid: coin.Txid,
|
||||
VOut: coin.VOut,
|
||||
},
|
||||
Descriptor: coin.Descriptor,
|
||||
Tapscripts: coin.Tapscripts,
|
||||
},
|
||||
ForfeitLeafHash: forfeitLeaf.TapHash(),
|
||||
})
|
||||
@@ -1158,7 +1150,7 @@ func (a *covenantlessArkClient) SetNostrNotificationRecipient(ctx context.Contex
|
||||
return err
|
||||
}
|
||||
|
||||
descriptorVtxos := make([]client.DescriptorVtxo, 0)
|
||||
descriptorVtxos := make([]client.TapscriptsVtxo, 0)
|
||||
for _, offchainAddr := range offchainAddrs {
|
||||
for _, vtxo := range spendableVtxos {
|
||||
vtxoAddr, err := vtxo.Address(a.AspPubkey, a.Network)
|
||||
@@ -1167,9 +1159,9 @@ func (a *covenantlessArkClient) SetNostrNotificationRecipient(ctx context.Contex
|
||||
}
|
||||
|
||||
if vtxoAddr == offchainAddr.Address {
|
||||
descriptorVtxos = append(descriptorVtxos, client.DescriptorVtxo{
|
||||
descriptorVtxos = append(descriptorVtxos, client.TapscriptsVtxo{
|
||||
Vtxo: vtxo,
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1187,35 +1179,24 @@ func (a *covenantlessArkClient) SetNostrNotificationRecipient(ctx context.Contex
|
||||
}
|
||||
|
||||
// validate the vtxo script type
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(v.Descriptor)
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(v.Tapscripts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var forfeitClosure bitcointree.Closure
|
||||
var signingPubkey string
|
||||
|
||||
if defaultVtxoScript, ok := vtxoScript.(*bitcointree.DefaultVtxoScript); ok {
|
||||
forfeitClosure = &bitcointree.MultisigClosure{
|
||||
Pubkey: defaultVtxoScript.Owner,
|
||||
AspPubkey: defaultVtxoScript.Asp,
|
||||
}
|
||||
|
||||
signingPubkey = hex.EncodeToString(schnorr.SerializePubKey(defaultVtxoScript.Owner))
|
||||
} else {
|
||||
return fmt.Errorf("unsupported vtxo script: %T", vtxoScript)
|
||||
}
|
||||
forfeitClosure := vtxoScript.ForfeitClosures()[0]
|
||||
|
||||
_, tapTree, err := vtxoScript.TapTree()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
forfeitLeaf, err := forfeitClosure.Leaf()
|
||||
forfeitScript, err := forfeitClosure.Script()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
forfeitLeaf := txscript.NewBaseTapLeaf(forfeitScript)
|
||||
merkleProof, err := tapTree.GetTaprootMerkleProof(forfeitLeaf.TapHash())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1236,7 +1217,7 @@ func (a *covenantlessArkClient) SetNostrNotificationRecipient(ctx context.Contex
|
||||
outpointBytes := append(txhash[:], voutBytes...)
|
||||
sigMsg := sha256.Sum256(outpointBytes)
|
||||
|
||||
sig, err := a.wallet.SignMessage(ctx, sigMsg[:], signingPubkey)
|
||||
sig, err := a.wallet.SignMessage(ctx, sigMsg[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1434,7 +1415,7 @@ func (a *covenantlessArkClient) sendOffchain(
|
||||
return "", fmt.Errorf("no offchain addresses found")
|
||||
}
|
||||
|
||||
vtxos := make([]client.DescriptorVtxo, 0)
|
||||
vtxos := make([]client.TapscriptsVtxo, 0)
|
||||
opts := &CoinSelectOptions{
|
||||
WithExpirySorting: withExpiryCoinselect}
|
||||
spendableVtxos, err := a.getVtxos(ctx, opts)
|
||||
@@ -1450,9 +1431,9 @@ func (a *covenantlessArkClient) sendOffchain(
|
||||
}
|
||||
|
||||
if vtxoAddr == offchainAddr.Address {
|
||||
vtxos = append(vtxos, client.DescriptorVtxo{
|
||||
vtxos = append(vtxos, client.TapscriptsVtxo{
|
||||
Vtxo: v,
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1464,7 +1445,7 @@ func (a *covenantlessArkClient) sendOffchain(
|
||||
}
|
||||
|
||||
var selectedBoardingCoins []types.Utxo
|
||||
var selectedCoins []client.DescriptorVtxo
|
||||
var selectedCoins []client.TapscriptsVtxo
|
||||
var changeAmount uint64
|
||||
|
||||
// if no receivers, self send all selected coins
|
||||
@@ -1513,7 +1494,7 @@ func (a *covenantlessArkClient) sendOffchain(
|
||||
Txid: coin.Txid,
|
||||
VOut: coin.VOut,
|
||||
},
|
||||
Descriptor: coin.Descriptor,
|
||||
Tapscripts: coin.Tapscripts,
|
||||
})
|
||||
}
|
||||
for _, boardingUtxo := range selectedBoardingCoins {
|
||||
@@ -1522,7 +1503,7 @@ func (a *covenantlessArkClient) sendOffchain(
|
||||
Txid: boardingUtxo.Txid,
|
||||
VOut: boardingUtxo.VOut,
|
||||
},
|
||||
Descriptor: boardingUtxo.Descriptor,
|
||||
Tapscripts: boardingUtxo.Tapscripts,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1567,21 +1548,11 @@ func (a *covenantlessArkClient) addInputs(
|
||||
return err
|
||||
}
|
||||
|
||||
vtxoScript, err := tree.ParseVtxoScript(offchain.Descriptor)
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(offchain.Tapscripts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var userPubkey, aspPubkey *secp256k1.PublicKey
|
||||
|
||||
switch s := vtxoScript.(type) {
|
||||
case *tree.DefaultVtxoScript:
|
||||
userPubkey = s.Owner
|
||||
aspPubkey = s.Asp
|
||||
default:
|
||||
return fmt.Errorf("unsupported vtxo script: %T", s)
|
||||
}
|
||||
|
||||
for _, utxo := range utxos {
|
||||
previousHash, err := chainhash.NewHashFromStr(utxo.Txid)
|
||||
if err != nil {
|
||||
@@ -1601,18 +1572,14 @@ func (a *covenantlessArkClient) addInputs(
|
||||
Sequence: sequence,
|
||||
})
|
||||
|
||||
vtxoScript := &bitcointree.DefaultVtxoScript{
|
||||
Owner: userPubkey,
|
||||
Asp: aspPubkey,
|
||||
ExitDelay: utxo.Delay,
|
||||
exitClosures := vtxoScript.ExitClosures()
|
||||
if len(exitClosures) <= 0 {
|
||||
return fmt.Errorf("no exit closures found")
|
||||
}
|
||||
|
||||
exitClosure := &bitcointree.CSVSigClosure{
|
||||
Pubkey: userPubkey,
|
||||
Seconds: uint(utxo.Delay),
|
||||
}
|
||||
exitClosure := exitClosures[0]
|
||||
|
||||
exitLeaf, err := exitClosure.Leaf()
|
||||
exitScript, err := exitClosure.Script()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1622,6 +1589,7 @@ func (a *covenantlessArkClient) addInputs(
|
||||
return err
|
||||
}
|
||||
|
||||
exitLeaf := txscript.NewBaseTapLeaf(exitScript)
|
||||
leafProof, err := taprootTree.GetTaprootMerkleProof(exitLeaf.TapHash())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get taproot merkle proof: %s", err)
|
||||
@@ -1644,7 +1612,7 @@ func (a *covenantlessArkClient) addInputs(
|
||||
func (a *covenantlessArkClient) handleRoundStream(
|
||||
ctx context.Context,
|
||||
paymentID string,
|
||||
vtxosToSign []client.DescriptorVtxo,
|
||||
vtxosToSign []client.TapscriptsVtxo,
|
||||
boardingUtxos []types.Utxo,
|
||||
receivers []client.Output,
|
||||
roundEphemeralKey *secp256k1.PrivateKey,
|
||||
@@ -1763,12 +1731,12 @@ func (a *covenantlessArkClient) handleRoundStream(
|
||||
func (a *covenantlessArkClient) handleRoundSigningStarted(
|
||||
ctx context.Context, ephemeralKey *secp256k1.PrivateKey, event client.RoundSigningStartedEvent,
|
||||
) (signerSession bitcointree.SignerSession, err error) {
|
||||
sweepClosure := bitcointree.CSVSigClosure{
|
||||
Pubkey: a.AspPubkey,
|
||||
Seconds: uint(a.RoundLifetime),
|
||||
sweepClosure := tree.CSVSigClosure{
|
||||
MultisigClosure: tree.MultisigClosure{PubKeys: []*secp256k1.PublicKey{a.AspPubkey}},
|
||||
Seconds: uint(a.RoundLifetime),
|
||||
}
|
||||
|
||||
sweepTapLeaf, err := sweepClosure.Leaf()
|
||||
script, err := sweepClosure.Script()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -1781,7 +1749,8 @@ func (a *covenantlessArkClient) handleRoundSigningStarted(
|
||||
sharedOutput := roundTx.UnsignedTx.TxOut[0]
|
||||
sharedOutputValue := sharedOutput.Value
|
||||
|
||||
sweepTapTree := txscript.AssembleTaprootScriptTree(*sweepTapLeaf)
|
||||
sweepTapLeaf := txscript.NewBaseTapLeaf(script)
|
||||
sweepTapTree := txscript.AssembleTaprootScriptTree(sweepTapLeaf)
|
||||
root := sweepTapTree.RootNode.TapHash()
|
||||
|
||||
signerSession = bitcointree.NewTreeSignerSession(
|
||||
@@ -1838,7 +1807,7 @@ func (a *covenantlessArkClient) handleRoundSigningNoncesGenerated(
|
||||
func (a *covenantlessArkClient) handleRoundFinalization(
|
||||
ctx context.Context,
|
||||
event client.RoundFinalizationEvent,
|
||||
vtxos []client.DescriptorVtxo,
|
||||
vtxos []client.TapscriptsVtxo,
|
||||
boardingUtxos []types.Utxo,
|
||||
receivers []client.Output,
|
||||
) ([]string, string, error) {
|
||||
@@ -1870,27 +1839,20 @@ func (a *covenantlessArkClient) handleRoundFinalization(
|
||||
}
|
||||
|
||||
for _, boardingUtxo := range boardingUtxos {
|
||||
boardingVtxoScript, err := bitcointree.ParseVtxoScript(boardingUtxo.Descriptor)
|
||||
boardingVtxoScript, err := bitcointree.ParseVtxoScript(boardingUtxo.Tapscripts)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var myPubkey *secp256k1.PublicKey
|
||||
|
||||
switch v := boardingVtxoScript.(type) {
|
||||
case *bitcointree.DefaultVtxoScript:
|
||||
myPubkey = v.Owner
|
||||
default:
|
||||
return nil, "", fmt.Errorf("unsupported boarding descriptor: %s", boardingUtxo.Descriptor)
|
||||
}
|
||||
|
||||
// add tapscript leaf
|
||||
forfeitClosure := &bitcointree.MultisigClosure{
|
||||
Pubkey: myPubkey,
|
||||
AspPubkey: a.AspPubkey,
|
||||
forfeitClosures := boardingVtxoScript.ForfeitClosures()
|
||||
if len(forfeitClosures) <= 0 {
|
||||
return nil, "", fmt.Errorf("no forfeit closures found")
|
||||
}
|
||||
|
||||
forfeitLeaf, err := forfeitClosure.Leaf()
|
||||
forfeitClosure := forfeitClosures[0]
|
||||
|
||||
forfeitScript, err := forfeitClosure.Script()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
@@ -1900,6 +1862,7 @@ func (a *covenantlessArkClient) handleRoundFinalization(
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
forfeitLeaf := txscript.NewBaseTapLeaf(forfeitScript)
|
||||
forfeitProof, err := taprootTree.GetTaprootMerkleProof(forfeitLeaf.TapHash())
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to get taproot merkle proof for boarding utxo: %s", err)
|
||||
@@ -2070,7 +2033,7 @@ func (a *covenantlessArkClient) validateOffChainReceiver(
|
||||
|
||||
func (a *covenantlessArkClient) createAndSignForfeits(
|
||||
ctx context.Context,
|
||||
vtxosToSign []client.DescriptorVtxo,
|
||||
vtxosToSign []client.TapscriptsVtxo,
|
||||
connectors []string,
|
||||
feeRate chainfee.SatPerKVByte,
|
||||
) ([]string, error) {
|
||||
@@ -2102,7 +2065,7 @@ func (a *covenantlessArkClient) createAndSignForfeits(
|
||||
}
|
||||
|
||||
for _, vtxo := range vtxosToSign {
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(vtxo.Descriptor)
|
||||
vtxoScript, err := bitcointree.ParseVtxoScript(vtxo.Tapscripts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2127,25 +2090,19 @@ func (a *covenantlessArkClient) createAndSignForfeits(
|
||||
Index: vtxo.VOut,
|
||||
}
|
||||
|
||||
var forfeitClosure bitcointree.Closure
|
||||
var witnessSize int
|
||||
|
||||
switch v := vtxoScript.(type) {
|
||||
case *bitcointree.DefaultVtxoScript:
|
||||
forfeitClosure = &bitcointree.MultisigClosure{
|
||||
Pubkey: v.Owner,
|
||||
AspPubkey: a.AspPubkey,
|
||||
}
|
||||
witnessSize = 64 * 2
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported vtxo script: %T", vtxoScript)
|
||||
forfeitClosures := vtxoScript.ForfeitClosures()
|
||||
if len(forfeitClosures) <= 0 {
|
||||
return nil, fmt.Errorf("no forfeit closures found")
|
||||
}
|
||||
|
||||
forfeitLeaf, err := forfeitClosure.Leaf()
|
||||
forfeitClosure := forfeitClosures[0]
|
||||
|
||||
forfeitScript, err := forfeitClosure.Script()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
forfeitLeaf := txscript.NewBaseTapLeaf(forfeitScript)
|
||||
leafProof, err := vtxoTapTree.GetTaprootMerkleProof(forfeitLeaf.TapHash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -2168,7 +2125,7 @@ func (a *covenantlessArkClient) createAndSignForfeits(
|
||||
RevealedScript: leafProof.Script,
|
||||
ControlBlock: ctrlBlock,
|
||||
},
|
||||
witnessSize,
|
||||
forfeitClosure.WitnessSize(),
|
||||
parsedScript.Class(),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -2221,25 +2178,23 @@ func (a *covenantlessArkClient) coinSelectOnchain(
|
||||
|
||||
fetchedUtxos := make([]types.Utxo, 0)
|
||||
for _, addr := range boardingAddrs {
|
||||
boardingScript, err := bitcointree.ParseVtxoScript(addr.Descriptor)
|
||||
boardingScript, err := bitcointree.ParseVtxoScript(addr.Tapscripts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var boardingTimeout uint
|
||||
|
||||
if defaultVtxo, ok := boardingScript.(*bitcointree.DefaultVtxoScript); ok {
|
||||
boardingTimeout = defaultVtxo.ExitDelay
|
||||
} else {
|
||||
return nil, 0, fmt.Errorf("unsupported boarding descriptor: %s", addr.Descriptor)
|
||||
boardingTimeout, err := boardingScript.SmallestExitDelay()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
utxos, err := a.explorer.GetUtxos(addr.Address)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
for _, utxo := range utxos {
|
||||
u := utxo.ToUtxo(boardingTimeout, addr.Descriptor)
|
||||
u := utxo.ToUtxo(boardingTimeout, addr.Tapscripts)
|
||||
if u.SpendableAt.Before(now) {
|
||||
fetchedUtxos = append(fetchedUtxos, u)
|
||||
}
|
||||
@@ -2275,7 +2230,7 @@ func (a *covenantlessArkClient) coinSelectOnchain(
|
||||
}
|
||||
|
||||
for _, utxo := range utxos {
|
||||
u := utxo.ToUtxo(uint(a.UnilateralExitDelay), addr.Descriptor)
|
||||
u := utxo.ToUtxo(uint(a.UnilateralExitDelay), addr.Tapscripts)
|
||||
if u.SpendableAt.Before(now) {
|
||||
fetchedUtxos = append(fetchedUtxos, u)
|
||||
}
|
||||
@@ -2407,7 +2362,7 @@ func (a *covenantlessArkClient) getAllBoardingUtxos(
|
||||
VOut: uint32(i),
|
||||
Amount: vout.Amount,
|
||||
CreatedAt: createdAt,
|
||||
Descriptor: addr.Descriptor,
|
||||
Tapscripts: addr.Tapscripts,
|
||||
Spent: spent,
|
||||
})
|
||||
}
|
||||
@@ -2427,17 +2382,14 @@ func (a *covenantlessArkClient) getClaimableBoardingUtxos(ctx context.Context, o
|
||||
claimable := make([]types.Utxo, 0)
|
||||
|
||||
for _, addr := range boardingAddrs {
|
||||
boardingScript, err := bitcointree.ParseVtxoScript(addr.Descriptor)
|
||||
boardingScript, err := bitcointree.ParseVtxoScript(addr.Tapscripts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var boardingTimeout uint
|
||||
|
||||
if defaultVtxo, ok := boardingScript.(*bitcointree.DefaultVtxoScript); ok {
|
||||
boardingTimeout = defaultVtxo.ExitDelay
|
||||
} else {
|
||||
return nil, fmt.Errorf("unsupported boarding descriptor: %s", addr.Descriptor)
|
||||
boardingTimeout, err := boardingScript.SmallestExitDelay()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
boardingUtxos, err := a.explorer.GetUtxos(addr.Address)
|
||||
@@ -2466,7 +2418,7 @@ func (a *covenantlessArkClient) getClaimableBoardingUtxos(ctx context.Context, o
|
||||
}
|
||||
}
|
||||
|
||||
u := utxo.ToUtxo(boardingTimeout, addr.Descriptor)
|
||||
u := utxo.ToUtxo(boardingTimeout, addr.Tapscripts)
|
||||
if u.SpendableAt.Before(now) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ type SpentStatus struct {
|
||||
SpentBy string `json:"txid,omitempty"`
|
||||
}
|
||||
|
||||
func (e ExplorerUtxo) ToUtxo(delay uint, descriptor string) types.Utxo {
|
||||
return newUtxo(e, delay, descriptor)
|
||||
func (e ExplorerUtxo) ToUtxo(delay uint, tapscripts []string) types.Utxo {
|
||||
return newUtxo(e, delay, tapscripts)
|
||||
}
|
||||
|
||||
type Explorer interface {
|
||||
@@ -415,7 +415,7 @@ func parseBitcoinTx(txStr string) (string, string, error) {
|
||||
return txhex, txid, nil
|
||||
}
|
||||
|
||||
func newUtxo(explorerUtxo ExplorerUtxo, delay uint, descriptor string) types.Utxo {
|
||||
func newUtxo(explorerUtxo ExplorerUtxo, delay uint, tapscripts []string) types.Utxo {
|
||||
utxoTime := explorerUtxo.Status.Blocktime
|
||||
createdAt := time.Unix(utxoTime, 0)
|
||||
if utxoTime == 0 {
|
||||
@@ -431,6 +431,6 @@ func newUtxo(explorerUtxo ExplorerUtxo, delay uint, descriptor string) types.Utx
|
||||
Delay: delay,
|
||||
SpendableAt: time.Unix(utxoTime, 0).Add(time.Duration(delay) * time.Second),
|
||||
CreatedAt: createdAt,
|
||||
Descriptor: descriptor,
|
||||
Tapscripts: tapscripts,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,12 @@ import (
|
||||
|
||||
func CoinSelect(
|
||||
boardingUtxos []types.Utxo,
|
||||
vtxos []client.DescriptorVtxo,
|
||||
vtxos []client.TapscriptsVtxo,
|
||||
amount,
|
||||
dust uint64,
|
||||
sortByExpirationTime bool,
|
||||
) ([]types.Utxo, []client.DescriptorVtxo, uint64, error) {
|
||||
selected, notSelected := make([]client.DescriptorVtxo, 0), make([]client.DescriptorVtxo, 0)
|
||||
) ([]types.Utxo, []client.TapscriptsVtxo, uint64, error) {
|
||||
selected, notSelected := make([]client.TapscriptsVtxo, 0), make([]client.TapscriptsVtxo, 0)
|
||||
selectedBoarding, notSelectedBoarding := make([]types.Utxo, 0), make([]types.Utxo, 0)
|
||||
selectedAmount := uint64(0)
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ark-network/ark/common/bitcointree"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/pkg/client-sdk/client"
|
||||
"github.com/ark-network/ark/pkg/client-sdk/explorer"
|
||||
@@ -172,7 +171,7 @@ func findCovenantlessSweepClosure(
|
||||
var seconds uint
|
||||
var sweepClosure *txscript.TapLeaf
|
||||
for _, tapLeaf := range tx.Inputs[0].TaprootLeafScript {
|
||||
closure := &bitcointree.CSVSigClosure{}
|
||||
closure := &tree.CSVSigClosure{}
|
||||
valid, err := closure.Decode(tapLeaf.Script)
|
||||
if err != nil {
|
||||
continue
|
||||
|
||||
@@ -113,7 +113,7 @@ type Utxo struct {
|
||||
Delay uint
|
||||
SpendableAt time.Time
|
||||
CreatedAt time.Time
|
||||
Descriptor string
|
||||
Tapscripts []string
|
||||
Spent bool
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/ark-network/ark/common/bitcointree"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
"github.com/ark-network/ark/pkg/client-sdk/explorer"
|
||||
"github.com/ark-network/ark/pkg/client-sdk/internal/utils"
|
||||
"github.com/ark-network/ark/pkg/client-sdk/types"
|
||||
@@ -43,7 +44,7 @@ func NewBitcoinWallet(
|
||||
|
||||
func (w *bitcoinWallet) GetAddresses(
|
||||
ctx context.Context,
|
||||
) ([]wallet.DescriptorAddress, []wallet.DescriptorAddress, []wallet.DescriptorAddress, error) {
|
||||
) ([]wallet.TapscriptsAddress, []wallet.TapscriptsAddress, []wallet.TapscriptsAddress, error) {
|
||||
offchainAddr, boardingAddr, err := w.getAddress(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
@@ -69,21 +70,21 @@ func (w *bitcoinWallet) GetAddresses(
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
offchainAddrs := []wallet.DescriptorAddress{
|
||||
offchainAddrs := []wallet.TapscriptsAddress{
|
||||
{
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
Address: encodedOffchainAddr,
|
||||
},
|
||||
}
|
||||
boardingAddrs := []wallet.DescriptorAddress{
|
||||
boardingAddrs := []wallet.TapscriptsAddress{
|
||||
{
|
||||
Descriptor: boardingAddr.Descriptor,
|
||||
Tapscripts: boardingAddr.Tapscripts,
|
||||
Address: boardingAddr.Address,
|
||||
},
|
||||
}
|
||||
redemptionAddrs := []wallet.DescriptorAddress{
|
||||
redemptionAddrs := []wallet.TapscriptsAddress{
|
||||
{
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
Address: redemptionAddr.EncodeAddress(),
|
||||
},
|
||||
}
|
||||
@@ -92,7 +93,7 @@ func (w *bitcoinWallet) GetAddresses(
|
||||
|
||||
func (w *bitcoinWallet) NewAddress(
|
||||
ctx context.Context, _ bool,
|
||||
) (*wallet.DescriptorAddress, *wallet.DescriptorAddress, error) {
|
||||
) (*wallet.TapscriptsAddress, *wallet.TapscriptsAddress, error) {
|
||||
offchainAddr, boardingAddr, err := w.getAddress(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -103,34 +104,34 @@ func (w *bitcoinWallet) NewAddress(
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &wallet.DescriptorAddress{
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
return &wallet.TapscriptsAddress{
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
Address: encodedOffchainAddr,
|
||||
}, boardingAddr, nil
|
||||
}
|
||||
|
||||
func (w *bitcoinWallet) NewAddresses(
|
||||
ctx context.Context, _ bool, num int,
|
||||
) ([]wallet.DescriptorAddress, []wallet.DescriptorAddress, error) {
|
||||
) ([]wallet.TapscriptsAddress, []wallet.TapscriptsAddress, error) {
|
||||
offchainAddr, boardingAddr, err := w.getAddress(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
offchainAddrs := make([]wallet.DescriptorAddress, 0, num)
|
||||
boardingAddrs := make([]wallet.DescriptorAddress, 0, num)
|
||||
offchainAddrs := make([]wallet.TapscriptsAddress, 0, num)
|
||||
boardingAddrs := make([]wallet.TapscriptsAddress, 0, num)
|
||||
for i := 0; i < num; i++ {
|
||||
encodedOffchainAddr, err := offchainAddr.Address.Encode()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
offchainAddrs = append(offchainAddrs, wallet.DescriptorAddress{
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
offchainAddrs = append(offchainAddrs, wallet.TapscriptsAddress{
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
Address: encodedOffchainAddr,
|
||||
})
|
||||
boardingAddrs = append(boardingAddrs, wallet.DescriptorAddress{
|
||||
Descriptor: boardingAddr.Descriptor,
|
||||
boardingAddrs = append(boardingAddrs, wallet.TapscriptsAddress{
|
||||
Tapscripts: boardingAddr.Tapscripts,
|
||||
Address: boardingAddr.Address,
|
||||
})
|
||||
}
|
||||
@@ -192,12 +193,12 @@ func (s *bitcoinWallet) SignTransaction(
|
||||
)
|
||||
|
||||
txsighashes := txscript.NewTxSigHashes(updater.Upsbt.UnsignedTx, prevoutFetcher)
|
||||
myPubkey := schnorr.SerializePubKey(s.walletData.Pubkey)
|
||||
|
||||
for i, input := range ptx.Inputs {
|
||||
if len(input.TaprootLeafScript) > 0 {
|
||||
pubkey := s.walletData.Pubkey
|
||||
for _, leaf := range input.TaprootLeafScript {
|
||||
closure, err := bitcointree.DecodeClosure(leaf.Script)
|
||||
closure, err := tree.DecodeClosure(leaf.Script)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -205,10 +206,20 @@ func (s *bitcoinWallet) SignTransaction(
|
||||
sign := false
|
||||
|
||||
switch c := closure.(type) {
|
||||
case *bitcointree.CSVSigClosure:
|
||||
sign = bytes.Equal(c.Pubkey.SerializeCompressed()[1:], pubkey.SerializeCompressed()[1:])
|
||||
case *bitcointree.MultisigClosure:
|
||||
sign = bytes.Equal(c.Pubkey.SerializeCompressed()[1:], pubkey.SerializeCompressed()[1:])
|
||||
case *tree.CSVSigClosure:
|
||||
for _, key := range c.MultisigClosure.PubKeys {
|
||||
if bytes.Equal(schnorr.SerializePubKey(key), myPubkey) {
|
||||
sign = true
|
||||
break
|
||||
}
|
||||
}
|
||||
case *tree.MultisigClosure:
|
||||
for _, key := range c.PubKeys {
|
||||
if bytes.Equal(schnorr.SerializePubKey(key), myPubkey) {
|
||||
sign = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sign {
|
||||
@@ -235,7 +246,7 @@ func (s *bitcoinWallet) SignTransaction(
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !sig.Verify(preimage, pubkey) {
|
||||
if !sig.Verify(preimage, s.walletData.Pubkey) {
|
||||
return "", fmt.Errorf("signature verification failed")
|
||||
}
|
||||
|
||||
@@ -244,7 +255,7 @@ func (s *bitcoinWallet) SignTransaction(
|
||||
}
|
||||
|
||||
updater.Upsbt.Inputs[i].TaprootScriptSpendSig = append(updater.Upsbt.Inputs[i].TaprootScriptSpendSig, &psbt.TaprootScriptSpendSig{
|
||||
XOnlyPubKey: schnorr.SerializePubKey(pubkey),
|
||||
XOnlyPubKey: myPubkey,
|
||||
LeafHash: hash.CloneBytes(),
|
||||
Signature: sig.Serialize(),
|
||||
SigHash: txscript.SigHashDefault,
|
||||
@@ -259,17 +270,12 @@ func (s *bitcoinWallet) SignTransaction(
|
||||
}
|
||||
|
||||
func (w *bitcoinWallet) SignMessage(
|
||||
ctx context.Context, message []byte, pubkey string,
|
||||
ctx context.Context, message []byte,
|
||||
) (string, error) {
|
||||
if w.IsLocked() {
|
||||
return "", fmt.Errorf("wallet is locked")
|
||||
}
|
||||
|
||||
walletPubkeyHex := hex.EncodeToString(schnorr.SerializePubKey(w.walletData.Pubkey))
|
||||
if walletPubkeyHex != pubkey {
|
||||
return "", fmt.Errorf("pubkey mismatch, cannot sign message")
|
||||
}
|
||||
|
||||
sig, err := schnorr.Sign(w.privateKey, message)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -278,14 +284,16 @@ func (w *bitcoinWallet) SignMessage(
|
||||
return hex.EncodeToString(sig.Serialize()), nil
|
||||
}
|
||||
|
||||
type addressWithTapscripts struct {
|
||||
Address common.Address
|
||||
Tapscripts []string
|
||||
}
|
||||
|
||||
func (w *bitcoinWallet) getAddress(
|
||||
ctx context.Context,
|
||||
) (
|
||||
*struct {
|
||||
Address common.Address
|
||||
Descriptor string
|
||||
},
|
||||
*wallet.DescriptorAddress,
|
||||
*addressWithTapscripts,
|
||||
*wallet.TapscriptsAddress,
|
||||
error,
|
||||
) {
|
||||
if w.walletData == nil {
|
||||
@@ -299,11 +307,11 @@ func (w *bitcoinWallet) getAddress(
|
||||
|
||||
netParams := utils.ToBitcoinNetwork(data.Network)
|
||||
|
||||
defaultVtxoScript := &bitcointree.DefaultVtxoScript{
|
||||
Asp: data.AspPubkey,
|
||||
Owner: w.walletData.Pubkey,
|
||||
ExitDelay: uint(data.UnilateralExitDelay),
|
||||
}
|
||||
defaultVtxoScript := bitcointree.NewDefaultVtxoScript(
|
||||
w.walletData.Pubkey,
|
||||
data.AspPubkey,
|
||||
uint(data.UnilateralExitDelay),
|
||||
)
|
||||
|
||||
vtxoTapKey, _, err := defaultVtxoScript.TapTree()
|
||||
if err != nil {
|
||||
@@ -316,16 +324,12 @@ func (w *bitcoinWallet) getAddress(
|
||||
VtxoTapKey: vtxoTapKey,
|
||||
}
|
||||
|
||||
myPubkeyStr := hex.EncodeToString(schnorr.SerializePubKey(w.walletData.Pubkey))
|
||||
descriptorStr := strings.ReplaceAll(
|
||||
data.BoardingDescriptorTemplate, "USER", myPubkeyStr,
|
||||
boardingVtxoScript := bitcointree.NewDefaultVtxoScript(
|
||||
w.walletData.Pubkey,
|
||||
data.AspPubkey,
|
||||
uint(data.UnilateralExitDelay*2),
|
||||
)
|
||||
|
||||
boardingVtxoScript, err := bitcointree.ParseVtxoScript(descriptorStr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
boardingTapKey, _, err := boardingVtxoScript.TapTree()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -339,14 +343,22 @@ func (w *bitcoinWallet) getAddress(
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &struct {
|
||||
Address common.Address
|
||||
Descriptor string
|
||||
}{
|
||||
*offchainAddress, defaultVtxoScript.ToDescriptor(),
|
||||
tapscripts, err := defaultVtxoScript.Encode()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
boardingTapscripts, err := boardingVtxoScript.Encode()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &addressWithTapscripts{
|
||||
Address: *offchainAddress,
|
||||
Tapscripts: tapscripts,
|
||||
},
|
||||
&wallet.DescriptorAddress{
|
||||
Descriptor: descriptorStr,
|
||||
&wallet.TapscriptsAddress{
|
||||
Tapscripts: boardingTapscripts,
|
||||
Address: boardingAddr.EncodeAddress(),
|
||||
},
|
||||
nil
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/ark-network/ark/common/tree"
|
||||
@@ -44,7 +43,7 @@ func NewLiquidWallet(
|
||||
|
||||
func (w *liquidWallet) GetAddresses(
|
||||
ctx context.Context,
|
||||
) ([]wallet.DescriptorAddress, []wallet.DescriptorAddress, []wallet.DescriptorAddress, error) {
|
||||
) ([]wallet.TapscriptsAddress, []wallet.TapscriptsAddress, []wallet.TapscriptsAddress, error) {
|
||||
offchainAddr, boardingAddr, err := w.getAddress(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
@@ -72,22 +71,22 @@ func (w *liquidWallet) GetAddresses(
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
offchainAddrs := []wallet.DescriptorAddress{
|
||||
offchainAddrs := []wallet.TapscriptsAddress{
|
||||
{
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
Address: encodedOffchainAddr,
|
||||
},
|
||||
}
|
||||
boardingAddrs := []wallet.DescriptorAddress{
|
||||
boardingAddrs := []wallet.TapscriptsAddress{
|
||||
{
|
||||
Descriptor: boardingAddr.Descriptor,
|
||||
Tapscripts: boardingAddr.Tapscripts,
|
||||
Address: boardingAddr.Address,
|
||||
},
|
||||
}
|
||||
|
||||
redemptionAddrs := []wallet.DescriptorAddress{
|
||||
redemptionAddrs := []wallet.TapscriptsAddress{
|
||||
{
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
Address: redemptionAddr,
|
||||
},
|
||||
}
|
||||
@@ -97,7 +96,7 @@ func (w *liquidWallet) GetAddresses(
|
||||
|
||||
func (w *liquidWallet) NewAddress(
|
||||
ctx context.Context, _ bool,
|
||||
) (*wallet.DescriptorAddress, *wallet.DescriptorAddress, error) {
|
||||
) (*wallet.TapscriptsAddress, *wallet.TapscriptsAddress, error) {
|
||||
offchainAddr, boardingAddr, err := w.getAddress(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -108,37 +107,37 @@ func (w *liquidWallet) NewAddress(
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &wallet.DescriptorAddress{
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
return &wallet.TapscriptsAddress{
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
Address: encodedOffchainAddr,
|
||||
}, &wallet.DescriptorAddress{
|
||||
Descriptor: boardingAddr.Descriptor,
|
||||
}, &wallet.TapscriptsAddress{
|
||||
Tapscripts: boardingAddr.Tapscripts,
|
||||
Address: boardingAddr.Address,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w *liquidWallet) NewAddresses(
|
||||
ctx context.Context, _ bool, num int,
|
||||
) ([]wallet.DescriptorAddress, []wallet.DescriptorAddress, error) {
|
||||
) ([]wallet.TapscriptsAddress, []wallet.TapscriptsAddress, error) {
|
||||
offchainAddr, boardingAddr, err := w.getAddress(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
offchainAddrs := make([]wallet.DescriptorAddress, 0, num)
|
||||
boardingAddrs := make([]wallet.DescriptorAddress, 0, num)
|
||||
offchainAddrs := make([]wallet.TapscriptsAddress, 0, num)
|
||||
boardingAddrs := make([]wallet.TapscriptsAddress, 0, num)
|
||||
for i := 0; i < num; i++ {
|
||||
encodedOffchainAddr, err := offchainAddr.Address.Encode()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
offchainAddrs = append(offchainAddrs, wallet.DescriptorAddress{
|
||||
Descriptor: offchainAddr.Descriptor,
|
||||
offchainAddrs = append(offchainAddrs, wallet.TapscriptsAddress{
|
||||
Tapscripts: offchainAddr.Tapscripts,
|
||||
Address: encodedOffchainAddr,
|
||||
})
|
||||
boardingAddrs = append(boardingAddrs, wallet.DescriptorAddress{
|
||||
Descriptor: boardingAddr.Descriptor,
|
||||
boardingAddrs = append(boardingAddrs, wallet.TapscriptsAddress{
|
||||
Tapscripts: boardingAddr.Tapscripts,
|
||||
Address: boardingAddr.Address,
|
||||
})
|
||||
}
|
||||
@@ -212,7 +211,7 @@ func (s *liquidWallet) SignTransaction(
|
||||
prevoutsAssets = append(prevoutsAssets, input.WitnessUtxo.Asset)
|
||||
}
|
||||
|
||||
serializedPubKey := s.walletData.Pubkey.SerializeCompressed()
|
||||
myPubkey := schnorr.SerializePubKey(s.walletData.Pubkey)
|
||||
|
||||
for i, input := range pset.Inputs {
|
||||
if len(input.TapLeafScript) > 0 {
|
||||
@@ -230,9 +229,19 @@ func (s *liquidWallet) SignTransaction(
|
||||
sign := false
|
||||
switch c := closure.(type) {
|
||||
case *tree.CSVSigClosure:
|
||||
sign = bytes.Equal(c.Pubkey.SerializeCompressed()[1:], serializedPubKey[1:])
|
||||
for _, key := range c.MultisigClosure.PubKeys {
|
||||
if bytes.Equal(schnorr.SerializePubKey(key), myPubkey) {
|
||||
sign = true
|
||||
break
|
||||
}
|
||||
}
|
||||
case *tree.MultisigClosure:
|
||||
sign = bytes.Equal(c.Pubkey.SerializeCompressed()[1:], serializedPubKey[1:])
|
||||
for _, key := range c.PubKeys {
|
||||
if bytes.Equal(schnorr.SerializePubKey(key), myPubkey) {
|
||||
sign = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sign {
|
||||
@@ -288,17 +297,12 @@ func (s *liquidWallet) SignTransaction(
|
||||
}
|
||||
|
||||
func (w *liquidWallet) SignMessage(
|
||||
ctx context.Context, message []byte, pubkey string,
|
||||
ctx context.Context, message []byte,
|
||||
) (string, error) {
|
||||
if w.IsLocked() {
|
||||
return "", fmt.Errorf("wallet is locked")
|
||||
}
|
||||
|
||||
walletPubkeyHex := hex.EncodeToString(schnorr.SerializePubKey(w.walletData.Pubkey))
|
||||
if walletPubkeyHex != pubkey {
|
||||
return "", fmt.Errorf("pubkey mismatch, cannot sign message")
|
||||
}
|
||||
|
||||
sig, err := schnorr.Sign(w.privateKey, message)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -310,11 +314,8 @@ func (w *liquidWallet) SignMessage(
|
||||
func (w *liquidWallet) getAddress(
|
||||
ctx context.Context,
|
||||
) (
|
||||
*struct {
|
||||
Address common.Address
|
||||
Descriptor string
|
||||
},
|
||||
*wallet.DescriptorAddress,
|
||||
*addressWithTapscripts,
|
||||
*wallet.TapscriptsAddress,
|
||||
error,
|
||||
) {
|
||||
if w.walletData == nil {
|
||||
@@ -328,11 +329,11 @@ func (w *liquidWallet) getAddress(
|
||||
|
||||
liquidNet := utils.ToElementsNetwork(data.Network)
|
||||
|
||||
vtxoScript := &tree.DefaultVtxoScript{
|
||||
Owner: w.walletData.Pubkey,
|
||||
Asp: data.AspPubkey,
|
||||
ExitDelay: uint(data.UnilateralExitDelay),
|
||||
}
|
||||
vtxoScript := tree.NewDefaultVtxoScript(
|
||||
w.walletData.Pubkey,
|
||||
data.AspPubkey,
|
||||
uint(data.UnilateralExitDelay),
|
||||
)
|
||||
|
||||
vtxoTapKey, _, err := vtxoScript.TapTree()
|
||||
if err != nil {
|
||||
@@ -345,22 +346,18 @@ func (w *liquidWallet) getAddress(
|
||||
VtxoTapKey: vtxoTapKey,
|
||||
}
|
||||
|
||||
myPubkeyStr := hex.EncodeToString(schnorr.SerializePubKey(w.walletData.Pubkey))
|
||||
descriptorStr := strings.ReplaceAll(
|
||||
data.BoardingDescriptorTemplate, "USER", myPubkeyStr,
|
||||
boardingVtxoScript := tree.NewDefaultVtxoScript(
|
||||
w.walletData.Pubkey,
|
||||
data.AspPubkey,
|
||||
uint(data.UnilateralExitDelay*2),
|
||||
)
|
||||
|
||||
onboardingScript, err := tree.ParseVtxoScript(descriptorStr)
|
||||
boardingTapKey, _, err := boardingVtxoScript.TapTree()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
tapKey, _, err := onboardingScript.TapTree()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
p2tr, err := payment.FromTweakedKey(tapKey, &liquidNet, nil)
|
||||
p2tr, err := payment.FromTweakedKey(boardingTapKey, &liquidNet, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -370,14 +367,21 @@ func (w *liquidWallet) getAddress(
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &struct {
|
||||
Address common.Address
|
||||
Descriptor string
|
||||
}{
|
||||
tapscripts, err := vtxoScript.Encode()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
boardingTapscripts, err := boardingVtxoScript.Encode()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &addressWithTapscripts{
|
||||
Address: *offchainAddr,
|
||||
Descriptor: vtxoScript.ToDescriptor(),
|
||||
}, &wallet.DescriptorAddress{
|
||||
Descriptor: descriptorStr,
|
||||
Tapscripts: tapscripts,
|
||||
}, &wallet.TapscriptsAddress{
|
||||
Tapscripts: boardingTapscripts,
|
||||
Address: boardingAddr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ const (
|
||||
SingleKeyWallet = "singlekey"
|
||||
)
|
||||
|
||||
type DescriptorAddress struct {
|
||||
Descriptor string
|
||||
type TapscriptsAddress struct {
|
||||
Tapscripts []string
|
||||
Address string
|
||||
}
|
||||
|
||||
@@ -25,18 +25,18 @@ type WalletService interface {
|
||||
IsLocked() bool
|
||||
GetAddresses(
|
||||
ctx context.Context,
|
||||
) (offchainAddresses, boardingAddresses, redemptionAddresses []DescriptorAddress, err error)
|
||||
) (offchainAddresses, boardingAddresses, redemptionAddresses []TapscriptsAddress, err error)
|
||||
NewAddress(
|
||||
ctx context.Context, change bool,
|
||||
) (offchainAddr, onchainAddr *DescriptorAddress, err error)
|
||||
) (offchainAddr, onchainAddr *TapscriptsAddress, err error)
|
||||
NewAddresses(
|
||||
ctx context.Context, change bool, num int,
|
||||
) (offchainAddresses, onchainAddresses []DescriptorAddress, err error)
|
||||
) (offchainAddresses, onchainAddresses []TapscriptsAddress, err error)
|
||||
SignTransaction(
|
||||
ctx context.Context, explorerSvc explorer.Explorer, tx string,
|
||||
) (signedTx string, err error)
|
||||
SignMessage(
|
||||
ctx context.Context, message []byte, pubkey string,
|
||||
ctx context.Context, message []byte,
|
||||
) (signature string, err error)
|
||||
Dump(ctx context.Context) (seed string, err error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user