2019-01-24 16:28:25 +03:00
|
|
|
package lnd
|
2017-07-14 22:05:55 +03:00
|
|
|
|
|
|
|
import (
|
2017-12-13 12:30:57 +03:00
|
|
|
"fmt"
|
2018-01-17 07:56:51 +03:00
|
|
|
"sync"
|
2018-07-26 20:18:59 +03:00
|
|
|
"sync/atomic"
|
2017-12-13 12:30:57 +03:00
|
|
|
|
2018-06-05 04:34:16 +03:00
|
|
|
"github.com/btcsuite/btcd/btcec"
|
|
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
|
|
"github.com/btcsuite/btcd/txscript"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
|
|
"github.com/btcsuite/btcutil"
|
2019-03-05 16:22:30 +03:00
|
|
|
"github.com/btcsuite/btcwallet/wallet/txauthor"
|
2019-01-16 17:47:43 +03:00
|
|
|
|
2018-07-18 05:23:47 +03:00
|
|
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
2019-01-16 17:47:43 +03:00
|
|
|
"github.com/lightningnetwork/lnd/input"
|
2018-07-18 05:23:47 +03:00
|
|
|
"github.com/lightningnetwork/lnd/keychain"
|
2019-02-20 04:06:00 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
2018-07-18 05:23:47 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
2017-07-14 22:05:55 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// The block height returned by the mock BlockChainIO's GetBestBlock.
|
|
|
|
const fundingBroadcastHeight = 123
|
|
|
|
|
|
|
|
type mockSigner struct {
|
|
|
|
key *btcec.PrivateKey
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx,
|
2019-01-16 17:47:43 +03:00
|
|
|
signDesc *input.SignDescriptor) ([]byte, error) {
|
2017-07-14 22:05:55 +03:00
|
|
|
amt := signDesc.Output.Value
|
|
|
|
witnessScript := signDesc.WitnessScript
|
|
|
|
privKey := m.key
|
|
|
|
|
2018-02-18 02:29:01 +03:00
|
|
|
if !privKey.PubKey().IsEqual(signDesc.KeyDesc.PubKey) {
|
2017-12-13 12:30:57 +03:00
|
|
|
return nil, fmt.Errorf("incorrect key passed")
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case signDesc.SingleTweak != nil:
|
2019-01-16 17:47:43 +03:00
|
|
|
privKey = input.TweakPrivKey(privKey,
|
2017-12-13 12:30:57 +03:00
|
|
|
signDesc.SingleTweak)
|
|
|
|
case signDesc.DoubleTweak != nil:
|
2019-01-16 17:47:43 +03:00
|
|
|
privKey = input.DeriveRevocationPrivKey(privKey,
|
2017-12-13 12:30:57 +03:00
|
|
|
signDesc.DoubleTweak)
|
|
|
|
}
|
|
|
|
|
2017-07-14 22:05:55 +03:00
|
|
|
sig, err := txscript.RawTxInWitnessSignature(tx, signDesc.SigHashes,
|
2017-11-06 16:29:05 +03:00
|
|
|
signDesc.InputIndex, amt, witnessScript, signDesc.HashType,
|
2017-07-14 22:05:55 +03:00
|
|
|
privKey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return sig[:len(sig)-1], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockSigner) ComputeInputScript(tx *wire.MsgTx,
|
2019-01-16 17:47:43 +03:00
|
|
|
signDesc *input.SignDescriptor) (*input.Script, error) {
|
2017-12-13 12:30:57 +03:00
|
|
|
|
|
|
|
// TODO(roasbeef): expose tweaked signer from lnwallet so don't need to
|
|
|
|
// duplicate this code?
|
|
|
|
|
|
|
|
privKey := m.key
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case signDesc.SingleTweak != nil:
|
2019-01-16 17:47:43 +03:00
|
|
|
privKey = input.TweakPrivKey(privKey,
|
2017-12-13 12:30:57 +03:00
|
|
|
signDesc.SingleTweak)
|
|
|
|
case signDesc.DoubleTweak != nil:
|
2019-01-16 17:47:43 +03:00
|
|
|
privKey = input.DeriveRevocationPrivKey(privKey,
|
2017-12-13 12:30:57 +03:00
|
|
|
signDesc.DoubleTweak)
|
|
|
|
}
|
|
|
|
|
2017-08-25 04:55:27 +03:00
|
|
|
witnessScript, err := txscript.WitnessSignature(tx, signDesc.SigHashes,
|
2017-12-13 12:30:57 +03:00
|
|
|
signDesc.InputIndex, signDesc.Output.Value, signDesc.Output.PkScript,
|
|
|
|
signDesc.HashType, privKey, true)
|
2017-07-14 22:05:55 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-01-16 17:47:43 +03:00
|
|
|
return &input.Script{
|
2017-07-14 22:05:55 +03:00
|
|
|
Witness: witnessScript,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type mockNotfier struct {
|
|
|
|
confChannel chan *chainntnfs.TxConfirmation
|
|
|
|
}
|
|
|
|
|
2018-05-31 08:18:44 +03:00
|
|
|
func (m *mockNotfier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
|
|
|
|
_ []byte, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) {
|
2017-07-14 22:05:55 +03:00
|
|
|
return &chainntnfs.ConfirmationEvent{
|
|
|
|
Confirmed: m.confChannel,
|
|
|
|
}, nil
|
|
|
|
}
|
2018-08-09 10:05:27 +03:00
|
|
|
func (m *mockNotfier) RegisterBlockEpochNtfn(
|
|
|
|
bestBlock *chainntnfs.BlockEpoch) (*chainntnfs.BlockEpochEvent, error) {
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 04:11:02 +03:00
|
|
|
return &chainntnfs.BlockEpochEvent{
|
|
|
|
Epochs: make(chan *chainntnfs.BlockEpoch),
|
|
|
|
Cancel: func() {},
|
|
|
|
}, nil
|
2017-07-14 22:05:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockNotfier) Start() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockNotfier) Stop() error {
|
|
|
|
return nil
|
|
|
|
}
|
2018-07-18 05:23:47 +03:00
|
|
|
func (m *mockNotfier) RegisterSpendNtfn(outpoint *wire.OutPoint, _ []byte,
|
2018-07-17 10:13:06 +03:00
|
|
|
heightHint uint32) (*chainntnfs.SpendEvent, error) {
|
2017-07-14 22:05:55 +03:00
|
|
|
return &chainntnfs.SpendEvent{
|
|
|
|
Spend: make(chan *chainntnfs.SpendDetail),
|
|
|
|
Cancel: func() {},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2017-12-13 12:30:57 +03:00
|
|
|
// mockSpendNotifier extends the mockNotifier so that spend notifications can be
|
|
|
|
// triggered and delivered to subscribers.
|
|
|
|
type mockSpendNotifier struct {
|
|
|
|
*mockNotfier
|
|
|
|
spendMap map[wire.OutPoint][]chan *chainntnfs.SpendDetail
|
2019-03-20 05:22:47 +03:00
|
|
|
spends map[wire.OutPoint]*chainntnfs.SpendDetail
|
2018-06-02 11:02:20 +03:00
|
|
|
mtx sync.Mutex
|
2017-12-13 12:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func makeMockSpendNotifier() *mockSpendNotifier {
|
|
|
|
return &mockSpendNotifier{
|
|
|
|
mockNotfier: &mockNotfier{
|
|
|
|
confChannel: make(chan *chainntnfs.TxConfirmation),
|
|
|
|
},
|
|
|
|
spendMap: make(map[wire.OutPoint][]chan *chainntnfs.SpendDetail),
|
2019-03-20 05:22:47 +03:00
|
|
|
spends: make(map[wire.OutPoint]*chainntnfs.SpendDetail),
|
2017-12-13 12:30:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockSpendNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
|
2018-07-18 05:23:47 +03:00
|
|
|
_ []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) {
|
2018-06-02 11:02:20 +03:00
|
|
|
m.mtx.Lock()
|
|
|
|
defer m.mtx.Unlock()
|
2017-12-13 12:30:57 +03:00
|
|
|
|
2019-03-20 05:22:47 +03:00
|
|
|
spendChan := make(chan *chainntnfs.SpendDetail, 1)
|
|
|
|
if detail, ok := m.spends[*outpoint]; ok {
|
|
|
|
// Deliver spend immediately if details are already known.
|
|
|
|
spendChan <- &chainntnfs.SpendDetail{
|
|
|
|
SpentOutPoint: detail.SpentOutPoint,
|
|
|
|
SpendingHeight: detail.SpendingHeight,
|
|
|
|
SpendingTx: detail.SpendingTx,
|
|
|
|
SpenderTxHash: detail.SpenderTxHash,
|
|
|
|
SpenderInputIndex: detail.SpenderInputIndex,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Otherwise, queue the notification for delivery if the spend
|
|
|
|
// is ever received.
|
|
|
|
m.spendMap[*outpoint] = append(m.spendMap[*outpoint], spendChan)
|
|
|
|
}
|
|
|
|
|
2017-12-13 12:30:57 +03:00
|
|
|
return &chainntnfs.SpendEvent{
|
|
|
|
Spend: spendChan,
|
|
|
|
Cancel: func() {
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Spend dispatches SpendDetails to all subscribers of the outpoint. The details
|
|
|
|
// will include the transaction and height provided by the caller.
|
|
|
|
func (m *mockSpendNotifier) Spend(outpoint *wire.OutPoint, height int32,
|
|
|
|
txn *wire.MsgTx) {
|
2018-06-02 11:02:20 +03:00
|
|
|
m.mtx.Lock()
|
|
|
|
defer m.mtx.Unlock()
|
2017-12-13 12:30:57 +03:00
|
|
|
|
2019-03-20 05:22:47 +03:00
|
|
|
txnHash := txn.TxHash()
|
|
|
|
details := &chainntnfs.SpendDetail{
|
|
|
|
SpentOutPoint: outpoint,
|
|
|
|
SpendingHeight: height,
|
|
|
|
SpendingTx: txn,
|
|
|
|
SpenderTxHash: &txnHash,
|
|
|
|
SpenderInputIndex: outpoint.Index,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cache details in case of late registration.
|
|
|
|
if _, ok := m.spends[*outpoint]; !ok {
|
|
|
|
m.spends[*outpoint] = details
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deliver any backlogged spend notifications.
|
2017-12-13 12:30:57 +03:00
|
|
|
if spendChans, ok := m.spendMap[*outpoint]; ok {
|
|
|
|
delete(m.spendMap, *outpoint)
|
|
|
|
for _, spendChan := range spendChans {
|
|
|
|
spendChan <- &chainntnfs.SpendDetail{
|
2019-03-20 05:22:47 +03:00
|
|
|
SpentOutPoint: details.SpentOutPoint,
|
|
|
|
SpendingHeight: details.SpendingHeight,
|
|
|
|
SpendingTx: details.SpendingTx,
|
|
|
|
SpenderTxHash: details.SpenderTxHash,
|
|
|
|
SpenderInputIndex: details.SpenderInputIndex,
|
2017-12-13 12:30:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-23 13:08:03 +03:00
|
|
|
type mockChainIO struct {
|
|
|
|
bestHeight int32
|
|
|
|
}
|
2017-07-14 22:05:55 +03:00
|
|
|
|
2018-08-24 16:30:23 +03:00
|
|
|
var _ lnwallet.BlockChainIO = (*mockChainIO)(nil)
|
|
|
|
|
2018-10-23 13:08:03 +03:00
|
|
|
func (m *mockChainIO) GetBestBlock() (*chainhash.Hash, int32, error) {
|
|
|
|
return activeNetParams.GenesisHash, m.bestHeight, nil
|
2017-07-14 22:05:55 +03:00
|
|
|
}
|
|
|
|
|
2018-07-18 05:23:47 +03:00
|
|
|
func (*mockChainIO) GetUtxo(op *wire.OutPoint, _ []byte,
|
2018-08-24 16:31:57 +03:00
|
|
|
heightHint uint32, _ <-chan struct{}) (*wire.TxOut, error) {
|
2017-07-14 22:05:55 +03:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*mockChainIO) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*mockChainIO) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// mockWalletController is used by the LightningWallet, and let us mock the
|
|
|
|
// interaction with the bitcoin network.
|
|
|
|
type mockWalletController struct {
|
|
|
|
rootKey *btcec.PrivateKey
|
|
|
|
prevAddres btcutil.Address
|
|
|
|
publishedTransactions chan *wire.MsgTx
|
2018-07-26 20:18:59 +03:00
|
|
|
index uint32
|
2019-07-11 14:14:37 +03:00
|
|
|
utxos []*lnwallet.Utxo
|
2017-07-14 22:05:55 +03:00
|
|
|
}
|
|
|
|
|
2017-12-22 08:23:24 +03:00
|
|
|
// BackEnd returns "mock" to signify a mock wallet controller.
|
|
|
|
func (*mockWalletController) BackEnd() string {
|
|
|
|
return "mock"
|
|
|
|
}
|
|
|
|
|
2017-07-14 22:05:55 +03:00
|
|
|
// FetchInputInfo will be called to get info about the inputs to the funding
|
|
|
|
// transaction.
|
|
|
|
func (*mockWalletController) FetchInputInfo(
|
|
|
|
prevOut *wire.OutPoint) (*wire.TxOut, error) {
|
|
|
|
txOut := &wire.TxOut{
|
|
|
|
Value: int64(10 * btcutil.SatoshiPerBitcoin),
|
|
|
|
PkScript: []byte("dummy"),
|
|
|
|
}
|
|
|
|
return txOut, nil
|
|
|
|
}
|
2018-02-18 02:40:10 +03:00
|
|
|
func (*mockWalletController) ConfirmedBalance(confs int32) (btcutil.Amount, error) {
|
2017-07-14 22:05:55 +03:00
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewAddress is called to get new addresses for delivery, change etc.
|
|
|
|
func (m *mockWalletController) NewAddress(addrType lnwallet.AddressType,
|
|
|
|
change bool) (btcutil.Address, error) {
|
|
|
|
addr, _ := btcutil.NewAddressPubKey(
|
|
|
|
m.rootKey.PubKey().SerializeCompressed(), &chaincfg.MainNetParams)
|
|
|
|
return addr, nil
|
|
|
|
}
|
2019-02-20 06:17:46 +03:00
|
|
|
func (*mockWalletController) LastUnusedAddress(addrType lnwallet.AddressType) (
|
|
|
|
btcutil.Address, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2018-09-28 06:58:46 +03:00
|
|
|
func (*mockWalletController) IsOurAddress(a btcutil.Address) bool {
|
|
|
|
return false
|
2017-07-14 22:05:55 +03:00
|
|
|
}
|
|
|
|
|
2017-11-23 22:38:08 +03:00
|
|
|
func (*mockWalletController) SendOutputs(outputs []*wire.TxOut,
|
2018-11-05 14:30:32 +03:00
|
|
|
_ lnwallet.SatPerKWeight) (*wire.MsgTx, error) {
|
2017-11-23 22:38:08 +03:00
|
|
|
|
2017-07-14 22:05:55 +03:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2019-03-05 16:22:30 +03:00
|
|
|
func (*mockWalletController) CreateSimpleTx(outputs []*wire.TxOut,
|
|
|
|
_ lnwallet.SatPerKWeight, _ bool) (*txauthor.AuthoredTx, error) {
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2017-07-14 22:05:55 +03:00
|
|
|
// ListUnspentWitness is called by the wallet when doing coin selection. We just
|
|
|
|
// need one unspent for the funding transaction.
|
2018-10-28 17:55:18 +03:00
|
|
|
func (m *mockWalletController) ListUnspentWitness(minconfirms,
|
|
|
|
maxconfirms int32) ([]*lnwallet.Utxo, error) {
|
2019-07-11 14:14:37 +03:00
|
|
|
|
|
|
|
// If the mock already has a list of utxos, return it.
|
|
|
|
if m.utxos != nil {
|
|
|
|
return m.utxos, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise create one to return.
|
2017-07-14 22:05:55 +03:00
|
|
|
utxo := &lnwallet.Utxo{
|
2017-10-03 04:52:45 +03:00
|
|
|
AddressType: lnwallet.WitnessPubKey,
|
|
|
|
Value: btcutil.Amount(10 * btcutil.SatoshiPerBitcoin),
|
|
|
|
PkScript: make([]byte, 22),
|
2017-07-14 22:05:55 +03:00
|
|
|
OutPoint: wire.OutPoint{
|
|
|
|
Hash: chainhash.Hash{},
|
2018-07-26 20:18:59 +03:00
|
|
|
Index: m.index,
|
2017-07-14 22:05:55 +03:00
|
|
|
},
|
|
|
|
}
|
2018-07-26 20:18:59 +03:00
|
|
|
atomic.AddUint32(&m.index, 1)
|
2017-07-14 22:05:55 +03:00
|
|
|
var ret []*lnwallet.Utxo
|
|
|
|
ret = append(ret, utxo)
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
func (*mockWalletController) ListTransactionDetails() ([]*lnwallet.TransactionDetail, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
func (*mockWalletController) LockOutpoint(o wire.OutPoint) {}
|
|
|
|
func (*mockWalletController) UnlockOutpoint(o wire.OutPoint) {}
|
|
|
|
func (m *mockWalletController) PublishTransaction(tx *wire.MsgTx) error {
|
|
|
|
m.publishedTransactions <- tx
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (*mockWalletController) SubscribeTransactions() (lnwallet.TransactionSubscription, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2017-12-10 10:42:46 +03:00
|
|
|
func (*mockWalletController) IsSynced() (bool, int64, error) {
|
|
|
|
return true, int64(0), nil
|
2017-07-14 22:05:55 +03:00
|
|
|
}
|
|
|
|
func (*mockWalletController) Start() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (*mockWalletController) Stop() error {
|
|
|
|
return nil
|
|
|
|
}
|
2018-01-17 07:56:51 +03:00
|
|
|
|
2018-02-18 02:29:01 +03:00
|
|
|
type mockSecretKeyRing struct {
|
|
|
|
rootKey *btcec.PrivateKey
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockSecretKeyRing) DeriveNextKey(keyFam keychain.KeyFamily) (keychain.KeyDescriptor, error) {
|
|
|
|
return keychain.KeyDescriptor{
|
|
|
|
PubKey: m.rootKey.PubKey(),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockSecretKeyRing) DeriveKey(keyLoc keychain.KeyLocator) (keychain.KeyDescriptor, error) {
|
|
|
|
return keychain.KeyDescriptor{
|
|
|
|
PubKey: m.rootKey.PubKey(),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockSecretKeyRing) DerivePrivKey(keyDesc keychain.KeyDescriptor) (*btcec.PrivateKey, error) {
|
|
|
|
return m.rootKey, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockSecretKeyRing) ScalarMult(keyDesc keychain.KeyDescriptor,
|
|
|
|
pubKey *btcec.PublicKey) ([]byte, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2018-01-17 07:56:51 +03:00
|
|
|
type mockPreimageCache struct {
|
|
|
|
sync.Mutex
|
2019-02-20 04:06:00 +03:00
|
|
|
preimageMap map[lntypes.Hash]lntypes.Preimage
|
2018-01-17 07:56:51 +03:00
|
|
|
}
|
|
|
|
|
2019-02-20 04:06:00 +03:00
|
|
|
func newMockPreimageCache() *mockPreimageCache {
|
|
|
|
return &mockPreimageCache{
|
|
|
|
preimageMap: make(map[lntypes.Hash]lntypes.Preimage),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockPreimageCache) LookupPreimage(hash lntypes.Hash) (lntypes.Preimage, bool) {
|
2018-01-17 07:56:51 +03:00
|
|
|
m.Lock()
|
|
|
|
defer m.Unlock()
|
|
|
|
|
2019-02-20 04:06:00 +03:00
|
|
|
p, ok := m.preimageMap[hash]
|
2018-01-17 07:56:51 +03:00
|
|
|
return p, ok
|
|
|
|
}
|
|
|
|
|
2019-02-20 04:06:00 +03:00
|
|
|
func (m *mockPreimageCache) AddPreimages(preimages ...lntypes.Preimage) error {
|
2018-01-17 07:56:51 +03:00
|
|
|
m.Lock()
|
|
|
|
defer m.Unlock()
|
|
|
|
|
2019-02-20 04:05:04 +03:00
|
|
|
for _, preimage := range preimages {
|
2019-02-20 04:06:00 +03:00
|
|
|
m.preimageMap[preimage.Hash()] = preimage
|
2019-02-20 04:05:04 +03:00
|
|
|
}
|
2018-01-17 07:56:51 +03:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|