mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-22 22:36:34 +01:00
Add tables for incoming and outgoing transactions
This commit is contained in:
parent
e4e9ec9db9
commit
bdd0468383
5 changed files with 223 additions and 0 deletions
|
@ -19,6 +19,8 @@ import org.bitcoins.core.hd.HDPurpose
|
|||
import org.bitcoins.core.hd.HDPurposes
|
||||
import org.bitcoins.core.hd.SegWitHDPath
|
||||
import slick.jdbc.GetResult
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.currency.Satoshis
|
||||
|
||||
abstract class DbCommonsColumnMappers {
|
||||
|
||||
|
@ -159,6 +161,9 @@ abstract class DbCommonsColumnMappers {
|
|||
MappedColumnType
|
||||
.base[ScriptType, String](_.toString, ScriptType.fromStringExn)
|
||||
|
||||
implicit val txMapper: BaseColumnType[Transaction] =
|
||||
MappedColumnType.base[Transaction, String](_.hex, Transaction.fromHex)
|
||||
|
||||
}
|
||||
|
||||
object DbCommonsColumnMappers extends DbCommonsColumnMappers
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package org.bitcoins.wallet.models
|
||||
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||
import org.bitcoins.testkit.core.gen.TransactionGenerators
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.wallet.fixtures._
|
||||
import org.bitcoins.testkit.wallet.WalletTestUtil
|
||||
import org.bitcoins.core.config.RegTest
|
||||
import org.bitcoins.core.crypto.ECPublicKey
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bouncycastle.crypto.tls.CertChainType
|
||||
import org.bitcoins.core.hd.HDChainType
|
||||
import org.bitcoins.core.hd.LegacyHDPath
|
||||
|
||||
class IncomingTransactionDAOTest
|
||||
extends BitcoinSWalletTest
|
||||
with IncomingTransactionDAOFixture {
|
||||
private def getTx: Transaction =
|
||||
TransactionGenerators.transaction
|
||||
.suchThat(_.outputs.nonEmpty)
|
||||
.sample
|
||||
.getOrElse(getTx)
|
||||
|
||||
it must "insert a incoming transaction and read it back with its address" in {
|
||||
daos =>
|
||||
val (txDao, addrDao) = daos
|
||||
implicit val walletconf: WalletAppConfig = config
|
||||
val accountDAO = AccountDAO()
|
||||
|
||||
val account = WalletTestUtil.firstAccountDb
|
||||
|
||||
val address = {
|
||||
val pub = ECPublicKey()
|
||||
val path =
|
||||
account.hdAccount
|
||||
.toChain(HDChainType.External)
|
||||
.toAddress(0)
|
||||
.toPath
|
||||
|
||||
AddressDbHelper.getAddress(pub, path, RegTest)
|
||||
}
|
||||
|
||||
val tx = getTx
|
||||
val txDb = IncomingTransaction(tx,
|
||||
confirmations = 3,
|
||||
scriptPubKey = address.scriptPubKey)
|
||||
import org.bitcoins.db.DbCommonsColumnMappers._
|
||||
for {
|
||||
_ <- accountDAO.create(account)
|
||||
createdAddress <- addrDao.create(address)
|
||||
createdTx <- txDao.create(txDb)
|
||||
txAndAddr <- txDao.withAddress(createdTx.transaction)
|
||||
} yield {
|
||||
txAndAddr match {
|
||||
case None => fail(s"Couldn't read back TX with address from DB!")
|
||||
case Some((foundTx, foundAddr)) =>
|
||||
// can't do just foundTx == txDb, ID's are different (None/Some(_))
|
||||
assert(foundTx.confirmations == txDb.confirmations)
|
||||
assert(foundTx.scriptPubKey == txDb.scriptPubKey)
|
||||
assert(foundTx.transaction == txDb.transaction)
|
||||
|
||||
assert(foundAddr == address)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package org.bitcoins.wallet.models
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.db.CRUDAutoInc
|
||||
import slick.jdbc.JdbcProfile
|
||||
import scala.concurrent.Future
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
|
||||
final case class IncomingTransactionDAO(profile: JdbcProfile)(
|
||||
implicit val ec: ExecutionContext,
|
||||
val appConfig: WalletAppConfig)
|
||||
extends CRUDAutoInc[IncomingTransaction] {
|
||||
|
||||
import profile.api._
|
||||
import org.bitcoins.db.DbCommonsColumnMappers._
|
||||
|
||||
override val table = TableQuery[IncomingTransactionTable]
|
||||
val addrTable = TableQuery[AddressTable]
|
||||
|
||||
/**
|
||||
* @param tx The transaction to look for
|
||||
* @return If found, the DB representation of the given TX,
|
||||
* along with the address it pays to
|
||||
*/
|
||||
def withAddress(
|
||||
tx: Transaction): Future[Option[(IncomingTransaction, AddressDb)]] = {
|
||||
withAddress(_ === tx)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rep A predicate to filter our incoming TXs on
|
||||
* @return The first TX that meets the predicate, along with
|
||||
* the address the transaction pays to
|
||||
*/
|
||||
def withAddress(pred: Rep[Transaction] => Rep[Boolean]): Future[
|
||||
Option[(IncomingTransaction, AddressDb)]] = {
|
||||
val query = {
|
||||
val filtered = table.filter(dbTx => pred(dbTx.transaction))
|
||||
filtered join addrTable on (_.scriptPubKey === _.scriptPubKey)
|
||||
}
|
||||
|
||||
database.run(query.result.headOption)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.bitcoins.wallet.models
|
||||
import scala.concurrent.ExecutionContext
|
||||
import slick.jdbc.JdbcProfile
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.db.CRUDAutoInc
|
||||
|
||||
final case class OutgoingTransactionDAO(profile: JdbcProfile)(
|
||||
implicit val ec: ExecutionContext,
|
||||
val appConfig: WalletAppConfig)
|
||||
extends CRUDAutoInc[OutgoingTransaction] {
|
||||
import profile.api._
|
||||
|
||||
override val table = TableQuery[OutgoingTransactionTable]
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package org.bitcoins.wallet.models
|
||||
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import slick.jdbc.SQLiteProfile.api._
|
||||
import slick.lifted.ProvenShape
|
||||
import org.bitcoins.db.TableAutoInc
|
||||
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||
import org.bitcoins.db.DbRowAutoInc
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
|
||||
/**
|
||||
* Database representation of transactions
|
||||
* relevant to our wallet.
|
||||
*/
|
||||
sealed trait TransactionDb[T <: TransactionDb[_]] extends DbRowAutoInc[T] {
|
||||
val transaction: Transaction
|
||||
lazy val txid: DoubleSha256DigestBE = transaction.txIdBE
|
||||
val confirmations: Int
|
||||
|
||||
}
|
||||
|
||||
/** Transactions our wallet has received */
|
||||
final case class IncomingTransaction(
|
||||
transaction: Transaction,
|
||||
scriptPubKey: ScriptPubKey,
|
||||
confirmations: Int,
|
||||
id: Option[Long] = None
|
||||
) extends TransactionDb[IncomingTransaction] {
|
||||
override def copyWithId(id: Long): IncomingTransaction = copy(id = Some(id))
|
||||
}
|
||||
|
||||
/** Transactions our wallet has sent */
|
||||
final case class OutgoingTransaction(
|
||||
transaction: Transaction,
|
||||
confirmations: Int,
|
||||
id: Option[Long] = None,
|
||||
utxoId: Option[Long] = None
|
||||
) extends TransactionDb[OutgoingTransaction] {
|
||||
override def copyWithId(id: Long): OutgoingTransaction = copy(id = Some(id))
|
||||
}
|
||||
|
||||
sealed abstract class TransactionTable[TxType <: TransactionDb[_]](
|
||||
tag: Tag,
|
||||
tableName: String)
|
||||
extends TableAutoInc[TxType](tag, tableName) {
|
||||
|
||||
import org.bitcoins.db.DbCommonsColumnMappers._
|
||||
|
||||
def transaction: Rep[Transaction] = column("transaction")
|
||||
|
||||
def confirmations: Rep[Int] = column("confirmations")
|
||||
|
||||
}
|
||||
|
||||
final case class IncomingTransactionTable(tag: Tag)
|
||||
extends TransactionTable[IncomingTransaction](tag, "incoming_transactions") {
|
||||
import org.bitcoins.db.DbCommonsColumnMappers._
|
||||
|
||||
// TODO: What happens if we get paid to multiple SPKs in the same
|
||||
// transaction? Need to make a table of SPKs, and map IDs in that
|
||||
// table to TXs in this table...
|
||||
def scriptPubKey: Rep[ScriptPubKey] = column("our_script_pubkey")
|
||||
|
||||
def fk_scriptPubKey =
|
||||
foreignKey("fk_script_pubkey",
|
||||
sourceColumns = scriptPubKey,
|
||||
targetTableQuery = TableQuery[AddressTable]) { addressTable =>
|
||||
addressTable.scriptPubKey
|
||||
}
|
||||
|
||||
override def * : ProvenShape[IncomingTransaction] =
|
||||
(transaction, scriptPubKey, confirmations, id.?) <> (IncomingTransaction.tupled, IncomingTransaction.unapply)
|
||||
|
||||
}
|
||||
|
||||
final case class OutgoingTransactionTable(tag: Tag)
|
||||
extends TransactionTable[OutgoingTransaction](tag, "outgoing_transactions") {
|
||||
import org.bitcoins.db.DbCommonsColumnMappers._
|
||||
|
||||
def utxoId: Rep[Long] = column("utxo_id", O.Unique)
|
||||
|
||||
def fk_utxo = {
|
||||
val utxoTable = TableQuery[UTXOSpendingInfoTable]
|
||||
foreignKey("fk_utxo", sourceColumns = utxoId, targetTableQuery = utxoTable) {
|
||||
_.id
|
||||
}
|
||||
}
|
||||
|
||||
override def * : ProvenShape[OutgoingTransaction] =
|
||||
(transaction, confirmations, id.?, utxoId.?) <> (OutgoingTransaction.tupled, OutgoingTransaction.unapply)
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue