114 lines
3.3 KiB
Go
114 lines
3.3 KiB
Go
// Copyright (c) 2017 The btcsuite developers
|
|
// Copyright (c) 2016 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package wallet
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/btcsuite/btcd/txscript"
|
|
"github.com/btcsuite/btcutil"
|
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
|
"github.com/btcsuite/btcwallet/walletdb"
|
|
)
|
|
|
|
// MakeMultiSigScript creates a multi-signature script that can be redeemed with
|
|
// nRequired signatures of the passed keys and addresses. If the address is a
|
|
// P2PKH address, the associated pubkey is looked up by the wallet if possible,
|
|
// otherwise an error is returned for a missing pubkey.
|
|
//
|
|
// This function only works with pubkeys and P2PKH addresses derived from them.
|
|
func (w *Wallet) MakeMultiSigScript(addrs []btcutil.Address, nRequired int) ([]byte, error) {
|
|
pubKeys := make([]*btcutil.AddressPubKey, len(addrs))
|
|
|
|
var dbtx walletdb.ReadTx
|
|
var addrmgrNs walletdb.ReadBucket
|
|
defer func() {
|
|
if dbtx != nil {
|
|
_ = dbtx.Rollback()
|
|
}
|
|
}()
|
|
|
|
// The address list will made up either of addreseses (pubkey hash), for
|
|
// which we need to look up the keys in wallet, straight pubkeys, or a
|
|
// mixture of the two.
|
|
for i, addr := range addrs {
|
|
switch addr := addr.(type) {
|
|
default:
|
|
return nil, errors.New("cannot make multisig script for " +
|
|
"a non-secp256k1 public key or P2PKH address")
|
|
|
|
case *btcutil.AddressPubKey:
|
|
pubKeys[i] = addr
|
|
|
|
case *btcutil.AddressPubKeyHash:
|
|
if dbtx == nil {
|
|
var err error
|
|
dbtx, err = w.db.BeginReadTx()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
addrmgrNs = dbtx.ReadBucket(waddrmgrNamespaceKey)
|
|
}
|
|
addrInfo, err := w.Manager.Address(addrmgrNs, addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
serializedPubKey := addrInfo.(waddrmgr.ManagedPubKeyAddress).
|
|
PubKey().SerializeCompressed()
|
|
|
|
pubKeyAddr, err := btcutil.NewAddressPubKey(
|
|
serializedPubKey, w.chainParams)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pubKeys[i] = pubKeyAddr
|
|
}
|
|
}
|
|
|
|
return txscript.MultiSigScript(pubKeys, nRequired)
|
|
}
|
|
|
|
// ImportP2SHRedeemScript adds a P2SH redeem script to the wallet.
|
|
func (w *Wallet) ImportP2SHRedeemScript(script []byte) (*btcutil.AddressScriptHash, error) {
|
|
var p2shAddr *btcutil.AddressScriptHash
|
|
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
|
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
|
|
|
// TODO(oga) blockstamp current block?
|
|
bs := &waddrmgr.BlockStamp{
|
|
Hash: *w.ChainParams().GenesisHash,
|
|
Height: 0,
|
|
}
|
|
|
|
// As this is a regular P2SH script, we'll import this into the
|
|
// BIP0044 scope.
|
|
bip44Mgr, err := w.Manager.FetchScopedKeyManager(
|
|
waddrmgr.KeyScopeBIP0084,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
addrInfo, err := bip44Mgr.ImportScript(addrmgrNs, script, bs)
|
|
if err != nil {
|
|
// Don't care if it's already there, but still have to
|
|
// set the p2shAddr since the address manager didn't
|
|
// return anything useful.
|
|
if waddrmgr.IsError(err, waddrmgr.ErrDuplicateAddress) {
|
|
// This function will never error as it always
|
|
// hashes the script to the correct length.
|
|
p2shAddr, _ = btcutil.NewAddressScriptHash(script,
|
|
w.chainParams)
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
p2shAddr = addrInfo.Address().(*btcutil.AddressScriptHash)
|
|
return nil
|
|
})
|
|
return p2shAddr, err
|
|
}
|