钱包 -> 账户、交易,存储。
架构设计:
钱包主要功能有两个:
存储数据。举例:
- 和“链”无关的数据 。如账户信息,地址、密钥、助记词等。
- 和“链”有关的数据 。如交易信息,历史交易记录,入账、出账等。
处理用户行为。举例:
- 核心功能 。创建账户,转账、查账,签名,导入、导出数据,查看交易汇总信息,查看单笔交易相关信息等。
- 非核心功能 。获取部分“链”相关数据,钱包的创建、恢复、修改密码,钱包设置,查看当前手续费,调用挖矿程序等。
根据上述“猜测”,我们来检查“钱包”所提供的各个方法,进行验证,就会发现实际情况和我们的预期“基本符合”。
数据需要存储,Wallet ORM,连接数据库 db Wallet 与软件其它模块的桥梁。
数据结构:
- 钱包相关
- 账户相关
- 交易相关
对外接口:
- 钱包模块 - 模块方法,数量少
- 钱包实例 - 实例方法,数量多
钱包
Wallet
// this is stored in disk in encrypted form
type Wallet struct {
Version semver.Version `json:"version"` // database version
Secret []byte `json:"secret"` // actual unlocker to the DB, depends on password from user, stored encrypted
// secret key used to encrypt all DB data ( both keys and values )
// this is always in encrypted form
KDF KDF `json:"kdf"`
account *Account //`json:"-"` // not serialized, we store an encrypted version // keys, seed language etc settings
Account_Encrypted []byte `json:"account_encrypted"`
pbkdf2_password []byte // used to encrypt metadata on updates
master_password []byte // single password which never changes
Daemon_Endpoint string `json:"-"` // endpoint used to communicate with daemon
Daemon_Height uint64 `json:"-"` // used to track daemon height ony if wallet in online
Daemon_TopoHeight int64 `json:"-"` // used to track daemon topo height ony if wallet in online
wallet_online_mode bool // set whether the mode is online or offline
// an offline wallet can be converted to online mode, calling.
// SetOffline() and vice versa using SetOnline
// used to create transaction with this fee rate,
//if this is lower than network, then created transaction will be rejected by network
dynamic_fees_per_kb uint64
quit chan bool // channel to quit any processing go routines
db *bolt.DB // access to DB
rpcserver *RPCServer // reference to RPCserver
id string // first 8 bytes of wallet address , to put into logs to identify different wallets in case many are active
transfer_mutex sync.Mutex // to avoid races within the transfer
//sync.Mutex // used to syncronise access
sync.RWMutex
}
涉及账户(安全,余额、转账等)、RPC服务、存储服务等。
KDF
// see this https://godoc.org/golang.org/x/crypto/pbkdf2
type KDF struct {
Hashfunction string `json:"hash"` //"SHA1" currently only sha1 is supported
Keylen int `json:"keylen"`
Iterations int `json:"iterations"`
Salt []byte `json:"salt"`
}
账户
Account
type Account struct {
Keys _Keys `json:"keys"`
SeedLanguage string `json:"seedlanguage"`
FeesMultiplier float32 `json:"feesmultiplier"` // fees multiplier accurate to 2 decimals
Mixin int `json:"mixin"` // default mixn to use for txs
ViewOnly bool `json:"viewonly"` // is this viewonly wallet
Index_Global uint64 `json:"index_global"` // till where the indexes have been processed, it must only increase and never decrease
Height uint64 `json:"height"` // block height till where blockchain has been scanned
TopoHeight int64 `json:"topoheight"` // block height till where blockchain has been scanned
//Wallet_Height uint64 `json:"wallet_height"`// used to track height till which we have scanned the inputs
balance_stale bool // whether the balance is stale
Balance_Mature uint64 `json:"balance_mature"` // total balance of account
Balance_Locked uint64 `json:"balance_locked"` // balance locked
random_percent uint64 // number of outputs to store within the db, for mixing, default is 10%
key_image_checklist map[crypto.Key]bool // key images which need to be monitored, this is updated when new funds arrive
//Outputs_Array []TX_Wallet_Data // all outputs found in the chain belonging to us, as found in chain
// uint64 si the Index_Global which is the unique number
//Outputs_Index map[uint64]bool // all outputs which are ours for deduplication
//Outputs_Ready map[uint64]TX_Wallet_Data // these outputs are ready for consumption ( maturity needs to be checked)
//Keyimages_Ready map[crypto.Key]bool // keyimages which are ready to get consumed, // we monitor them to find which
//Outputs_Consumed map[crypto.Key]TX_Wallet_Data // the key is the keyimage
//Random_Outputs map[uint64]TX_Wallet_Data // random ring members
//Random_Outputs_Recent map[uint64]TX_Wallet_Data // random ring members from recent blocks
//Ring_Members map[uint64]bool // ring members
sync.Mutex // syncronise modifications to this structure
}
_Keys
type _Keys struct {
Spendkey_Secret crypto.Key `json:"spendkey_secret"`
Spendkey_Public crypto.Key `json:"spendkey_public"`
Viewkey_Secret crypto.Key `json:"viewkey_secret"`
Viewkey_Public crypto.Key `json:"viewkey_public"`
}
交易
Ring_Member
// all random outputs are stored within wallet in this form
// to be used as ring members
type Ring_Member struct { // structure size is around 74 bytes
InKey ringct.CtKey `msgpack:"K"`
Index_Global uint64 `msgpack:"I"`
Height uint64 `msgpack:"H"`
Unlock_Height uint64 `msgpack:"U,omitempty"` // this is mostly empty
Sigtype uint64 `msgpack:"S,omitempty"` // this is empty for miner tx
}
TX_Wallet_Data
// this structure is kept by wallet
type TX_Wallet_Data struct {
TXdata globals.TX_Output_Data `msgpack:"txdata"` // all the fields of output data
WAmount uint64 `msgpack:"wamount"` // actual amount, in case of miner it is verbatim, for other cases it decrypted
WKey ringct.CtKey `msgpack:"wkey"` // key which is used to later send this specific output
WKimage crypto.Key `msgpack:"wkimage"` // key image which gets consumed when this output is spent
WSpent bool `msgpack:"wspent"` // whether this output has been spent
WSpentPool bool //`msgpack:""`// we built and send out a tx , but it has not been mined
WPaymentID []byte `msgpack:"wpaymentid"` // payment if if present and decrypted if required
WSecretTXkey crypto.Key `msgpack:"wsecrettxkey"` // tx secret which can be be used to prove that the funds have been spent
}
Entry
// 本地存储的转账记录
// Show_Transfers 时用到
type Entry struct {
Index_Global uint64 `json:"index_global"`
Height uint64 `json:"height"`
TopoHeight int64 `json:"topoheight"`
TXID crypto.Hash `json:"txid"`
Amount uint64 `json:"amount"`
PaymentID []byte `json:"payment_id"`
Status byte `json:"status"`
Unlock_Time uint64 `json:"unlock_time"`
Time time.Time `json:"time"`
Secret_TX_Key string `json:"secret_tx_key"` // can be used to prove if available
Details structures.Outgoing_Transfer_Details `json:"details"` // actual details if available
}