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

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, &ltxObj)
if err != nil {
a.log.Warnln("TX Object: ", string(rbytes), err)
}
return &ptxObj, &ltxObj
}
/* 在钱包导入时调用 */
// 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
}