-
Notifications
You must be signed in to change notification settings - Fork 88
/
db.go
146 lines (119 loc) · 5.73 KB
/
db.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright 2018 Shift Devices AG
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package transactions
import (
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/digitalbitbox/bitbox-wallet-app/backend/coins/btc/blockchain"
"github.com/digitalbitbox/bitbox-wallet-app/backend/coins/btc/types"
"github.com/digitalbitbox/bitbox-wallet-app/util/errp"
)
// DBTxInfo contains data stored for a wallet transaction.
type DBTxInfo struct {
Tx *wire.MsgTx `json:"Tx"`
Height int `json:"Height"`
Addresses map[string]bool `json:"addresses"`
Verified *bool `json:"Verified"`
HeaderTimestamp *time.Time `json:"ts"`
CreatedTimestamp *time.Time `json:"created"`
// TxHash is the same as Tx.TxHash(), but since we already have this value in the database, it
// is faster to access it this way than to recompute it. It is not serialized and stored in the
// database.
TxHash chainhash.Hash `json:"-"`
}
// DBTxInterface needs to be implemented to persist all wallet/transaction related data.
type DBTxInterface interface {
// Commit closes the transaction, writing the changes.
Commit() error
// Rollback closes the transaction without writing anything and can be called safely after
// Commit().
Rollback()
// PutTx stores a transaction and it's height (according to
// https://github.com/kyuupichan/electrumx/blob/46f245891cb62845f9eec0f9549526a7e569eb03/docs/protocol-basics.rst#status).
PutTx(txHash chainhash.Hash, tx *wire.MsgTx, height int) error
// DeleteTx deletes a transaction (nothing happens if not found).
DeleteTx(txHash chainhash.Hash)
// AddAddressToTx adds an address associated with a transaction. Retrieve them with `TxInfo()`.
AddAddressToTx(chainhash.Hash, blockchain.ScriptHashHex) error
RemoveAddressFromTx(chainhash.Hash, blockchain.ScriptHashHex) (bool, error)
// TxInfo retrieves all data stored with for a transaction. nil is returned if not found.
TxInfo(chainhash.Hash) (*DBTxInfo, error)
// Transactions retrieves all stored transaction hashes.
Transactions() ([]chainhash.Hash, error)
// UnverifiedTransactions retrieves all stored transaction hashes of unverified transactions.
UnverifiedTransactions() ([]chainhash.Hash, error)
// MarkTxVerified marks a tx as verified. Stores timestamp of the header this tx appears in.
MarkTxVerified(txHash chainhash.Hash, headerTimestamp time.Time) error
// PutInput stores a transaction input. It is referenced by the output it spends. The
// transaction hash of the transaction this input was found in is recorded. TODO: store slice of
// inputs along with the txhash they appear in. If there are more than one, a double spend is
// detected.
PutInput(wire.OutPoint, chainhash.Hash) error
// Input retrieves an input. `nil, nil` is returned if not found.
Input(wire.OutPoint) (*chainhash.Hash, error)
// DeleteInput deletes an input (nothing happens if not found).
DeleteInput(wire.OutPoint)
// PutOutput stores an Output.
PutOutput(wire.OutPoint, *wire.TxOut) error
// Output retrieves an output. `nil, nil` is returned if not found.
Output(wire.OutPoint) (*wire.TxOut, error)
Outputs() (map[wire.OutPoint]*wire.TxOut, error)
// DeleteOutput deletes an output (nothing happens if not found).
DeleteOutput(wire.OutPoint)
// PutAddressHistory stores an address history.
PutAddressHistory(blockchain.ScriptHashHex, blockchain.TxHistory) error
// AddressHistory retrieves an address history. If not found, returns an empty history.
AddressHistory(blockchain.ScriptHashHex) (blockchain.TxHistory, error)
// PutGapLimits stores the gap limits for receive and change addresses.
PutGapLimits(types.GapLimits) error
// GapLimits returns the gap limit for receive and change addresses.
// If none have been stored before, the default zero value is returned.
GapLimits() (types.GapLimits, error)
}
// DBInterface can be implemented by database backends to open database transactions.
type DBInterface interface {
// Begin starts a DB transaction. Apply `defer tx.Rollback()` in any case after. Use
// `tx.Commit()` to commit the write operations. If `writable` is true, write-operations are
// permitted, and concurrent write- or read-transactions are serialized by blocking. If
// `writable` is false, concurrent read-transactions are performed without blocking, unless
// there is an ongoing write-transaction.
Begin(writable bool) (DBTxInterface, error)
Close() error
}
// DBUpdate updates the database. All changes are rolled back on error. The transaction is committed
// if the callback does not return an error.
func DBUpdate(db DBInterface, f func(DBTxInterface) error) error {
dbTx, err := db.Begin(true)
if err != nil {
return err
}
defer dbTx.Rollback()
if err := f(dbTx); err != nil {
return err
}
return dbTx.Commit()
}
// DBView reads from the database. Any write-operations on the database transaction will result in
// an error. The return value of the callback is passed as the return value of the whole function
// for ease of use.
func DBView[R any](db DBInterface, f func(DBTxInterface) (R, error)) (R, error) {
dbTx, err := db.Begin(false)
if err != nil {
var empty R
return empty, errp.WithStack(err)
}
defer dbTx.Rollback()
return f(dbTx)
}