forked from go/golangs_learn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
8.9 KiB
250 lines
8.9 KiB
package app
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"net/http"
|
|
|
|
"github.com/grvlle/constellation_wallet/backend/models"
|
|
)
|
|
|
|
// formTXChain 在所有账户中保留区块链的顺序。
|
|
// 同时调用创建块对象的方法(将 json 写入文件),
|
|
// 以及将它们推送到网络的方法 (HTTP POST)。
|
|
// 为了保持顺序,formTXChain 将轮询最后发送的 TX 的 Failed 状态。
|
|
// 如果最后一个 TX 失败,它将切换顺序以考虑不会中断链。
|
|
// 这意味着所有创建数据块的失败尝试也会存储在数据库中,并带有
|
|
// Failed (失败) 状态。
|
|
func (a *WalletApplication) formTXChain(amount int64, fee int64, address string, ptxObj *Transaction, ltxObj *Transaction) {
|
|
|
|
statusLastTX := models.TXHistory{}
|
|
if err := a.DB.Last(&statusLastTX).Error; err != nil {
|
|
a.log.Warnln("未检测到此钱包的先前 TX。原因: ", err)
|
|
}
|
|
|
|
if statusLastTX.Failed {
|
|
a.log.Warnln("Previous Transaction 具有 failed 状态。适应...", statusLastTX.Failed)
|
|
}
|
|
// 查询此钱包的先前交易数量。
|
|
numberOfTX := a.DB.Model(&a.wallet).Association("TXHistory").Count()
|
|
|
|
// 第一个 TX 不包含 TXref
|
|
if numberOfTX == 0 {
|
|
a.log.Infoln("Detected that this is the first TX sent from this key.")
|
|
a.produceTXObject(amount, fee, address, a.paths.LastTXFile, a.paths.EmptyTXFile)
|
|
a.sendTransaction(a.paths.LastTXFile)
|
|
return
|
|
}
|
|
|
|
a.log.Infoln("Number of previous TX's detected: ", numberOfTX)
|
|
|
|
// 手动控制第二个 TX,确保顺序如下
|
|
if numberOfTX == 1 {
|
|
|
|
// 如果第一个事务失败,则强制执行顺序。
|
|
if statusLastTX.Failed {
|
|
a.log.Warnln("Detected that the previous transaction failed.")
|
|
a.produceTXObject(amount, fee, address, a.paths.LastTXFile, a.paths.EmptyTXFile)
|
|
a.sendTransaction(a.paths.LastTXFile)
|
|
return
|
|
}
|
|
|
|
// 检查已写入 PrevTXFile 并需要引用的边缘情况。
|
|
// 当导入具有 1 个 tx 的钱包时,会发生这种情况。
|
|
prevTXFileContents := a.loadTXFromFile(a.paths.PrevTXFile)
|
|
if a.WalletImported && prevTXFileContents != "" {
|
|
a.log.Warnln("One previous transaction has been imported. Using that as reference.")
|
|
a.produceTXObject(amount, fee, address, a.paths.LastTXFile, a.paths.PrevTXFile)
|
|
a.sendTransaction(a.paths.LastTXFile)
|
|
return
|
|
}
|
|
|
|
a.produceTXObject(amount, fee, address, a.paths.PrevTXFile, a.paths.LastTXFile)
|
|
a.sendTransaction(a.paths.PrevTXFile)
|
|
return
|
|
}
|
|
|
|
//返回具有最高序数的 TX 对象(最高确定它是要引用还是引用另一个 tx)
|
|
newTX := a.determineBlockOrder(ptxObj, ltxObj)
|
|
|
|
// 如果最后一个 TX 处于 failed 状态,我们将重置顺序。
|
|
if newTX == a.paths.PrevTXFile && statusLastTX.Failed {
|
|
a.produceTXObject(amount, fee, address, a.paths.LastTXFile, a.paths.PrevTXFile)
|
|
a.sendTransaction(a.paths.LastTXFile)
|
|
return
|
|
}
|
|
|
|
if newTX != a.paths.PrevTXFile && !statusLastTX.Failed {
|
|
a.produceTXObject(amount, fee, address, a.paths.LastTXFile, a.paths.PrevTXFile)
|
|
a.sendTransaction(a.paths.LastTXFile)
|
|
return
|
|
}
|
|
a.produceTXObject(amount, fee, address, a.paths.PrevTXFile, a.paths.LastTXFile)
|
|
a.sendTransaction(a.paths.PrevTXFile)
|
|
}
|
|
|
|
func (a *WalletApplication) determineBlockOrder(ptxObj, ltxObj *Transaction) string {
|
|
// 较高的序数将始终是带有 TX Ref 的 TX。
|
|
a.log.Info("Last TX Ordinal: ", ltxObj.LastTxRef.Ordinal, " Previous TX Ordinal: ", ptxObj.LastTxRef.Ordinal)
|
|
if ltxObj.LastTxRef.Ordinal > ptxObj.LastTxRef.Ordinal {
|
|
return a.paths.PrevTXFile
|
|
}
|
|
return a.paths.LastTXFile
|
|
|
|
}
|
|
|
|
// convertToTXObject 获取 prev_tx 的 Path 并last_tx文件,并返回一个指向两个 workable 对象的指针。
|
|
func (a *WalletApplication) convertToTXObject(ptx, ltx string) (*Transaction, *Transaction) {
|
|
var ptxObj Transaction
|
|
var ltxObj Transaction
|
|
|
|
rbytes := []byte(ptx)
|
|
lbytes := []byte(ltx)
|
|
|
|
err := json.Unmarshal(rbytes, &ptxObj)
|
|
if err != nil {
|
|
a.log.Warnln("TX Object: ", string(rbytes), err)
|
|
}
|
|
err = json.Unmarshal(lbytes, <xObj)
|
|
if err != nil {
|
|
a.log.Warnln("TX Object: ", string(rbytes), err)
|
|
}
|
|
return &ptxObj, <xObj
|
|
}
|
|
|
|
/* 在钱包导入时调用 */
|
|
|
|
// TXReference 用于解析导入的 wallet 之前的 tx。
|
|
type TXReference struct {
|
|
Hash string `json:"hash"`
|
|
Amount int64 `json:"amount"`
|
|
Receiver string `json:"receiver"`
|
|
Sender string `json:"sender"`
|
|
Fee int `json:"fee"`
|
|
IsDummy bool `json:"isDummy"`
|
|
LastTransactionRef struct {
|
|
PrevHash string `json:"prevHash"`
|
|
Ordinal int `json:"ordinal"`
|
|
} `json:"lastTransactionRef"`
|
|
SnapshotHash string `json:"snapshotHash"`
|
|
CheckpointBlock string `json:"checkpointBlock"`
|
|
TransactionOriginal struct {
|
|
Edge struct {
|
|
ObservationEdge struct {
|
|
Parents []struct {
|
|
HashReference string `json:"hashReference"`
|
|
HashType string `json:"hashType"`
|
|
BaseHash string `json:"baseHash"`
|
|
} `json:"parents"`
|
|
Data struct {
|
|
HashReference string `json:"hashReference"`
|
|
HashType string `json:"hashType"`
|
|
BaseHash string `json:"baseHash"`
|
|
} `json:"data"`
|
|
} `json:"observationEdge"`
|
|
SignedObservationEdge struct {
|
|
SignatureBatch struct {
|
|
Hash string `json:"hash"`
|
|
Signatures []struct {
|
|
Signature string `json:"signature"`
|
|
ID struct {
|
|
Hex string `json:"hex"`
|
|
} `json:"id"`
|
|
} `json:"signatures"`
|
|
} `json:"signatureBatch"`
|
|
} `json:"signedObservationEdge"`
|
|
Data struct {
|
|
Amount int64 `json:"amount"`
|
|
LastTxRef struct {
|
|
PrevHash string `json:"prevHash"`
|
|
Ordinal int `json:"ordinal"`
|
|
} `json:"lastTxRef"`
|
|
Fee interface{} `json:"fee"`
|
|
Salt int64 `json:"salt"`
|
|
} `json:"data"`
|
|
} `json:"edge"`
|
|
LastTxRef struct {
|
|
PrevHash string `json:"prevHash"`
|
|
Ordinal int `json:"ordinal"`
|
|
} `json:"lastTxRef"`
|
|
IsDummy bool `json:"isDummy"`
|
|
IsTest bool `json:"isTest"`
|
|
} `json:"transactionOriginal"`
|
|
}
|
|
|
|
// rebuildTxChainState 将查询区块浏览器以获取交易并将其写入 a.paths.PrevTXFile。 这将允许导入的钱包引用最后发送的交易。
|
|
func (a *WalletApplication) rebuildTxChainState(lastTXHash string) error {
|
|
a.log.Info("向区块浏览器发送 API 调用: " + a.Network.BlockExplorer.Handles.Transactions)
|
|
|
|
resp, err := http.Get(a.Network.BlockExplorer.URL + a.Network.BlockExplorer.Handles.Transactions + lastTXHash)
|
|
if err != nil {
|
|
a.log.Errorln("发送 HTTP 请求失败。原因: ", err)
|
|
if err := a.DB.Model(&a.wallet).Where("wallet_alias = ?", a.wallet.WalletAlias).Delete(&a.wallet).Error; err != nil {
|
|
a.log.Errorln("导入失败时无法删除 wallet。原因: ", err)
|
|
return err
|
|
}
|
|
a.LoginError("无法从 BlockExplorer 收集以前的 TX。请检查您的互联网连接。")
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.Body != nil {
|
|
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
a.LoginError("无法从 BlockExplorer 收集以前的 TX。请稍后重试。")
|
|
a.log.Errorln("无法从 BlockExplorer 收集以前的 TX。原因: ", err)
|
|
}
|
|
ok, e := a.verifyAPIResponse(bodyBytes)
|
|
// 当没有找到以前的交易时,BlockExplorer 返回以下字符串
|
|
if !ok && e != "Cannot find transaction" {
|
|
a.log.Errorln("API 返回以下错误", e)
|
|
// 如果无法导入最后一笔交易,请从数据库中删除钱包并注销。
|
|
if err := a.DB.Model(&a.wallet).Where("wallet_alias = ?", a.wallet.WalletAlias).Delete(&a.wallet).Error; err != nil {
|
|
a.log.Errorln("导入失败时无法删除 wallet。原因: ", err)
|
|
return err
|
|
}
|
|
a.log.Panicln("无法导入以前的交易") // TODO: logout user from wallet
|
|
a.LoginError("钱包导入失败。请检查您的互联网连接,然后重试。")
|
|
return err
|
|
}
|
|
|
|
// Parsing JSON object to TXReference ->
|
|
lastTX := TXReference{}
|
|
err = json.Unmarshal(bodyBytes, &lastTX)
|
|
if err != nil {
|
|
a.log.Errorln("无法从区块浏览器获取 TX 历史记录。原因: ", err)
|
|
a.sendError("无法从区块浏览器获取 TX 历史记录。原因: ", err)
|
|
return err
|
|
}
|
|
// Marshal so that we can unmarshat into tx object ->
|
|
b, err := json.Marshal(lastTX.TransactionOriginal)
|
|
if err != nil {
|
|
a.log.Errorln("无法解析最后一个交易哈希。原因: ", err)
|
|
a.sendError("无法从区块浏览器获取 TX 历史记录。原因: ", err)
|
|
return err
|
|
}
|
|
|
|
// Populating tx object ->
|
|
tx := Transaction{}
|
|
err = json.Unmarshal(b, &tx)
|
|
if err != nil {
|
|
a.log.Errorln("无法解析最后一个交易哈希。原因: ", err)
|
|
a.sendError("无法从区块浏览器获取 TX 历史记录。原因: ", err)
|
|
return err
|
|
}
|
|
|
|
// Converting to json
|
|
txBytes, err := json.Marshal(tx)
|
|
if err != nil {
|
|
a.log.Errorln("无法解析最后一个交易哈希。原因: ", err)
|
|
a.sendError("无法从区块浏览器获取 TX 历史记录。原因: ", err)
|
|
return err
|
|
}
|
|
|
|
err = WriteToFile(a.paths.PrevTXFile, txBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|