mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-22 14:22:39 +01:00
Electrum: do not persist transaction locks (#953)
Locks held on utxos that are used in unpublished funding transactions should not be persisted. If the app is stopped before the funding transaction has been published the channel is forgotten and so should be locks on its funding tx utxos.
This commit is contained in:
parent
9032da5326
commit
6afe28d147
2 changed files with 63 additions and 36 deletions
|
@ -211,7 +211,7 @@ object SqliteWalletDb {
|
|||
("history" | historyCodec) ::
|
||||
("proofs" | proofsCodec) ::
|
||||
("pendingTransactions" | listOfN(uint16, txCodec)) ::
|
||||
("locks" | setCodec(txCodec))).as[PersistentData]
|
||||
("locks" | provide(Set.empty[Transaction]))).as[PersistentData]
|
||||
|
||||
def serialize(data: PersistentData): Array[Byte] = persistentDataCodec.encode(data).require.toByteArray
|
||||
|
||||
|
|
|
@ -17,11 +17,16 @@
|
|||
package fr.acinq.eclair.blockchain.electrum.db.sqlite
|
||||
|
||||
import fr.acinq.bitcoin.{Block, BlockHeader, OutPoint, Satoshi, Transaction, TxIn, TxOut}
|
||||
import fr.acinq.eclair.TestConstants
|
||||
import fr.acinq.eclair.{TestConstants, randomBytes, randomBytes32}
|
||||
import fr.acinq.eclair.blockchain.electrum.ElectrumClient
|
||||
import fr.acinq.eclair.blockchain.electrum.ElectrumClient.GetMerkleResponse
|
||||
import fr.acinq.eclair.blockchain.electrum.ElectrumWallet.PersistentData
|
||||
import fr.acinq.eclair.blockchain.electrum.db.sqlite.SqliteWalletDb.version
|
||||
import fr.acinq.eclair.wire.ChannelCodecs.txCodec
|
||||
import org.scalatest.FunSuite
|
||||
import scodec.Codec
|
||||
import scodec.bits.BitVector
|
||||
import scodec.codecs.{constant, listOfN, provide, uint16}
|
||||
|
||||
import scala.util.Random
|
||||
|
||||
|
@ -34,6 +39,36 @@ class SqliteWalletDbSpec extends FunSuite {
|
|||
if (acc.size == n) acc else makeHeaders(n, acc :+ makeChildHeader(acc.last))
|
||||
}
|
||||
|
||||
def randomTransaction = Transaction(version = 2,
|
||||
txIn = TxIn(OutPoint(randomBytes32, random.nextInt(100)), signatureScript = Nil, sequence = TxIn.SEQUENCE_FINAL) :: Nil,
|
||||
txOut = TxOut(Satoshi(random.nextInt(10000000)), randomBytes(20)) :: Nil,
|
||||
0L
|
||||
)
|
||||
|
||||
def randomHeight = if (random.nextBoolean()) random.nextInt(500000) else -1
|
||||
|
||||
def randomHistoryItem = ElectrumClient.TransactionHistoryItem(randomHeight, randomBytes32)
|
||||
|
||||
def randomHistoryItems = (0 to random.nextInt(100)).map(_ => randomHistoryItem).toList
|
||||
|
||||
def randomProof = GetMerkleResponse(randomBytes32, ((0 until 10).map(_ => randomBytes32)).toList, random.nextInt(100000), 0)
|
||||
|
||||
def randomPersistentData = {
|
||||
val transactions = for (i <- 0 until random.nextInt(100)) yield randomTransaction
|
||||
|
||||
PersistentData(
|
||||
accountKeysCount = 10,
|
||||
changeKeysCount = 10,
|
||||
status = (for (i <- 0 until random.nextInt(100)) yield randomBytes32 -> random.nextInt(100000).toHexString).toMap,
|
||||
transactions = transactions.map(tx => tx.hash -> tx).toMap,
|
||||
heights = transactions.map(tx => tx.hash -> randomHeight).toMap,
|
||||
history = (for (i <- 0 until random.nextInt(100)) yield randomBytes32 -> randomHistoryItems).toMap,
|
||||
proofs = (for (i <- 0 until random.nextInt(100)) yield randomBytes32 -> randomProof).toMap,
|
||||
pendingTransactions = transactions.toList,
|
||||
locks = (for (i <- 0 until random.nextInt(10)) yield randomTransaction).toSet
|
||||
)
|
||||
}
|
||||
|
||||
test("add/get/list headers") {
|
||||
val db = new SqliteWalletDb(TestConstants.sqliteInMemory())
|
||||
val headers = makeHeaders(100)
|
||||
|
@ -59,46 +94,38 @@ class SqliteWalletDbSpec extends FunSuite {
|
|||
|
||||
test("serialize persistent data") {
|
||||
val db = new SqliteWalletDb(TestConstants.sqliteInMemory())
|
||||
|
||||
import fr.acinq.eclair.{randomBytes, randomBytes32}
|
||||
|
||||
def randomTransaction = Transaction(version = 2,
|
||||
txIn = TxIn(OutPoint(randomBytes32, random.nextInt(100)), signatureScript = Nil, sequence = TxIn.SEQUENCE_FINAL) :: Nil,
|
||||
txOut = TxOut(Satoshi(random.nextInt(10000000)), randomBytes(20)) :: Nil,
|
||||
0L
|
||||
)
|
||||
|
||||
def randomHeight = if (random.nextBoolean()) random.nextInt(500000) else -1
|
||||
|
||||
def randomHistoryItem = ElectrumClient.TransactionHistoryItem(randomHeight, randomBytes32)
|
||||
|
||||
def randomHistoryItems = (0 to random.nextInt(100)).map(_ => randomHistoryItem).toList
|
||||
|
||||
def randomProof = GetMerkleResponse(randomBytes32, ((0 until 10).map(_ => randomBytes32)).toList, random.nextInt(100000), 0)
|
||||
|
||||
def randomPersistentData = {
|
||||
val transactions = for (i <- 0 until random.nextInt(100)) yield randomTransaction
|
||||
|
||||
PersistentData(
|
||||
accountKeysCount = 10,
|
||||
changeKeysCount = 10,
|
||||
status = (for (i <- 0 until random.nextInt(100)) yield randomBytes32 -> random.nextInt(100000).toHexString).toMap,
|
||||
transactions = transactions.map(tx => tx.hash -> tx).toMap,
|
||||
heights = transactions.map(tx => tx.hash -> randomHeight).toMap,
|
||||
history = (for (i <- 0 until random.nextInt(100)) yield randomBytes32 -> randomHistoryItems).toMap,
|
||||
proofs = (for (i <- 0 until random.nextInt(100)) yield randomBytes32 -> randomProof).toMap,
|
||||
pendingTransactions = transactions.toList,
|
||||
locks = (for (i <- 0 until random.nextInt(10)) yield randomTransaction).toSet
|
||||
)
|
||||
}
|
||||
|
||||
assert(db.readPersistentData() == None)
|
||||
|
||||
for (i <- 0 until 50) {
|
||||
val data = randomPersistentData
|
||||
db.persist(data)
|
||||
val Some(check) = db.readPersistentData()
|
||||
assert(check === data)
|
||||
assert(check === data.copy(locks = Set.empty[Transaction]))
|
||||
}
|
||||
}
|
||||
|
||||
test("read old persistent data") {
|
||||
import scodec.codecs._
|
||||
import SqliteWalletDb._
|
||||
import fr.acinq.eclair.wire.ChannelCodecs._
|
||||
|
||||
val oldPersistentDataCodec: Codec[PersistentData] = (
|
||||
("version" | constant(BitVector.fromInt(version))) ::
|
||||
("accountKeysCount" | int32) ::
|
||||
("changeKeysCount" | int32) ::
|
||||
("status" | statusCodec) ::
|
||||
("transactions" | transactionsCodec) ::
|
||||
("heights" | heightsCodec) ::
|
||||
("history" | historyCodec) ::
|
||||
("proofs" | proofsCodec) ::
|
||||
("pendingTransactions" | listOfN(uint16, txCodec)) ::
|
||||
("locks" | setCodec(txCodec))).as[PersistentData]
|
||||
|
||||
for (i <- 0 until 50) {
|
||||
val data = randomPersistentData
|
||||
val encoded = oldPersistentDataCodec.encode(data).require
|
||||
val decoded = persistentDataCodec.decode(encoded).require.value
|
||||
assert(decoded === data.copy(locks = Set.empty[Transaction]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue