diff --git a/core/tx_pool.go b/core/tx_pool.go index ab0b01ab..0cab113c 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -25,6 +25,14 @@ import ( "sync" "time" + "github.com/sero-cash/go-czero-import/c_superzk" + "github.com/sero-cash/go-sero/zero/txs/stx/stx_v0" + + "github.com/sero-cash/go-sero/zero/localdb" + + "github.com/sero-cash/go-czero-import/c_type" + "github.com/sero-cash/go-sero/zero/txtool" + "github.com/sero-cash/go-sero/crypto" "github.com/sero-cash/go-sero/zero/stake" "github.com/sero-cash/go-sero/zero/txs/stx" @@ -113,6 +121,8 @@ type TxPoolConfig struct { GlobalQueue uint64 // Maximum number of non-executable transaction slots for all accounts Lifetime time.Duration // Maximum amount of time non-executable transaction are queued + + StartLight bool } // DefaultTxPoolConfig contains the default configurations for the transaction @@ -140,6 +150,169 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig { return conf } +func TxToOut(tx types.Transaction) (result []txtool.Out, txHash c_type.Uint256) { + txCommonHash := tx.Hash() + copy(txHash[:], txCommonHash[:]) + ztxs := tx.GetZZSTX() + if ztxs != nil { + tx1 := tx.GetZZSTX().Tx1 + for index := range tx1.Outs_P { + out := txtool.Out{} + rootState := localdb.RootState{} + os := localdb.OutState{} + out_p := &tx1.Outs_P[index] + if c_superzk.IsSzkPKr(&tx1.Outs_P[index].PKr) { + os.Out_P = out_p + os.GenRootCM() + } else { + os.Out_O = &stx_v0.Out_O{ + Addr: out_p.PKr, + Asset: out_p.Asset, + Memo: out_p.Memo, + } + os.GenRootCM() + } + rootState.OS = os + rootState.TxHash = txHash + out.State = rootState + result = append(result, out) + + } + + for index := range tx1.Outs_C { + out := txtool.Out{} + rootState := localdb.RootState{} + os := localdb.OutState{} + os.Out_C = &tx1.Outs_C[index] + os.GenRootCM() + rootState.OS = os + rootState.TxHash = txHash + out.State = rootState + result = append(result, out) + } + } + return +} + +type PKrTxOuts map[c_type.PKr]map[c_type.Uint256]*TxOutInfo + +func (p PKrTxOuts) AddPendingTxOut(tx types.Transaction) { + + txOuts, _ := TxToOut(tx) + time := uint64(time.Now().Unix()) + for index := range txOuts { + p.AddOut(tx.From(), tx.Gas(), tx.Gas(), tx.GasPrice(), 0, common.Hash{}, time, txOuts[index]) + } + +} + +func (p PKrTxOuts) AddImmatureTxOut(tx types.Transaction, blockNumber uint64, blockHash common.Hash, time uint64) { + + txOuts, _ := TxToOut(tx) + for index := range txOuts { + p.AddOut(tx.From(), tx.Gas(), tx.Gas(), tx.GasPrice(), blockNumber, blockHash, time, txOuts[index]) + } + +} + +func (p PKrTxOuts) AddOut( + from common.Address, + gas uint64, gasUsed uint64, + gasPrice *big.Int, + blockNumber uint64, + blockHash common.Hash, time uint64, + out txtool.Out) { + pkr := out.State.OS.ToPKr() + txHash := out.State.TxHash + if pkr != nil { + if _, ok := p[*pkr]; ok { + if _, ok := p[*pkr][txHash]; ok { + p[*pkr][txHash].addOut(txHash, from, gas, gasUsed, gasPrice, blockNumber, blockHash, time, out) + } else { + txOutInfo := &TxOutInfo{OutExists: map[c_type.Uint256]bool{}} + txOutInfo.addOut(txHash, from, gas, gasUsed, gasPrice, blockNumber, blockHash, time, out) + p[*pkr][txHash] = txOutInfo + } + + } else { + txOutInfo := &TxOutInfo{OutExists: map[c_type.Uint256]bool{}} + txOutInfo.addOut(txHash, from, gas, gasUsed, gasPrice, blockNumber, blockHash, time, out) + txOutMap := make(map[c_type.Uint256]*TxOutInfo) + txOutMap[txHash] = txOutInfo + p[*pkr] = txOutMap + } + } + +} + +func (p PKrTxOuts) delPendintTxOut(tx types.Transaction) { + txOuts, txHash := TxToOut(tx) + for index := range txOuts { + out := txOuts[index] + pkr := out.State.OS.ToPKr() + if pkr != nil { + delete(p[*pkr], txHash) + if len(p[*pkr]) == 0 { + delete(p, *pkr) + } + + } + } +} + +func (p PKrTxOuts) delPkrTxOut(pkr c_type.PKr, txHash c_type.Uint256) { + + delete(p[pkr], txHash) + if len(p[pkr]) == 0 { + delete(p, pkr) + } +} + +type TxOutInfo struct { + TxHash c_type.Uint256 + BlockNumber uint64 + BlockHash common.Hash + GasUsed uint64 + Gas uint64 + GasPrice *big.Int + From common.Address + Time uint64 + Outs []txtool.Out + OutExists map[c_type.Uint256]bool +} + +func (t *TxOutInfo) addOut(txHash c_type.Uint256, from common.Address, gas, gasUsed uint64, gasPrice *big.Int, blockNumber uint64, blockHash common.Hash, time uint64, out txtool.Out) { + t.TxHash = txHash + t.BlockNumber = blockNumber + t.BlockHash = blockHash + t.Gas = gas + t.GasUsed = gasUsed + t.GasPrice = gasPrice + t.From = from + t.Time = time + + var existsKey c_type.Uint256 + + if out.State.OS.Out_O != nil { + existsKey = out.State.OS.Out_O.ToHash() + } + + if out.State.OS.Out_P != nil { + existsKey = out.State.OS.Out_P.ToHash() + } + + if out.State.OS.Out_C != nil { + existsKey = out.State.OS.Out_C.Tx1_Hash() + } + + if _, ok := t.OutExists[existsKey]; !ok { + t.Outs = append(t.Outs, out) + t.OutExists[existsKey] = true + + } + +} + // TxPool contains all currently known transactions. Transactions // enter the pool when they are received from the network or submitted // locally. They exit the pool when they are included in the blockchain. @@ -175,6 +348,8 @@ type TxPool struct { wg sync.WaitGroup // for shutdown sync + pkrTxOuts PKrTxOuts + homestead bool } @@ -195,6 +370,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize), gasPrice: new(big.Int).SetUint64(config.PriceLimit), } + pool.pkrTxOuts = make(map[c_type.PKr]map[c_type.Uint256]*TxOutInfo) pool.locals = newAccountSet() pool.priced = newTxPricedList(pool.all) pool.newQueue = newTxPricedList(newTxLookup()) @@ -259,6 +435,9 @@ func (pool *TxPool) loop() { } for _, tx := range drop { pool.removeTx(tx.Hash()) + if pool.canAddPkrTx() { + pool.pkrTxOuts.delPendintTxOut(*tx) + } } dropFaileds := []common.Hash{} @@ -293,6 +472,14 @@ func (pool *TxPool) lockedReset(oldHead, newHead *types.Header) { pool.reset(oldHead, newHead) } +func (pool *TxPool) canAddPkrTx() bool { + difference := time.Now().Unix() - pool.chain.CurrentBlock().Time().Int64() + if difference > 10*60 { + return false + } + return pool.config.StartLight +} + // reset retrieves the current state of the blockchain and ensures the content // of the transaction pool is valid with regard to the chain state. func (pool *TxPool) reset(oldHead, newHead *types.Header) { @@ -323,6 +510,11 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { } for add.NumberU64() > rem.NumberU64() { included = append(included, add.Transactions()...) + if pool.canAddPkrTx() { + for _, tx := range add.Transactions() { + pool.pkrTxOuts.AddImmatureTxOut(*tx, add.Number().Uint64(), add.Hash(), add.Time().Uint64()) + } + } if add = pool.chain.GetBlock(add.ParentHash(), add.NumberU64()-1); add == nil { log.Error("Unrooted new chain seen by tx pool", "block", newHead.Number, "hash", newHead.Hash()) return @@ -335,6 +527,12 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { return } included = append(included, add.Transactions()...) + if pool.canAddPkrTx() { + for _, tx := range add.Transactions() { + pool.pkrTxOuts.AddImmatureTxOut(*tx, add.Number().Uint64(), add.Hash(), add.Time().Uint64()) + } + } + if add = pool.chain.GetBlock(add.ParentHash(), add.NumberU64()-1); add == nil { log.Error("Unrooted new chain seen by tx pool", "block", newHead.Number, "hash", newHead.Hash()) return @@ -359,6 +557,11 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { if len(included) == 0 { add := pool.chain.GetBlock(newHead.Hash(), newHead.Number.Uint64()) + if pool.canAddPkrTx() { + for _, tx := range add.Transactions() { + pool.pkrTxOuts.AddImmatureTxOut(*tx, add.Number().Uint64(), add.Hash(), add.Time().Uint64()) + } + } included = append(included, add.Transactions()...) } @@ -446,6 +649,26 @@ func (pool *TxPool) Content() (types.Transactions, types.Transactions) { return pending, queued } +func (pool *TxPool) PendingOuts(pkr c_type.PKr) map[c_type.Uint256]*TxOutInfo { + pool.mu.Lock() + defer pool.mu.Unlock() + return pool.pkrTxOuts[pkr] +} + +func (pool *TxPool) DelMaturedOuts(pkr c_type.PKr, txHash c_type.Uint256, currentNum uint64) { + pool.mu.Lock() + defer pool.mu.Unlock() + if txHashMap, ok := pool.pkrTxOuts[pkr]; ok { + for k, v := range txHashMap { + if v.BlockNumber != 0 && v.BlockNumber < currentNum { + pool.pkrTxOuts.delPkrTxOut(pkr, k) + } + } + } + pool.pkrTxOuts.delPkrTxOut(pkr, txHash) + +} + // Pending retrieves all currently processable transactions, groupped by origin // account and sorted by nonce. The returned transaction set is a copy and can be // freely modified by calling code. @@ -626,6 +849,9 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) { drop := pool.priced.Discard(pool.gasPrice, pool.all.Count()-int(pool.config.GlobalSlots+pool.config.GlobalQueue-1)) for _, tx := range drop { pool.removeTx(tx.Hash()) + if pool.canAddPkrTx() { + pool.pkrTxOuts.delPendintTxOut(*tx) + } log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "priced", tx.GasPrice()) } } @@ -634,6 +860,9 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) { if err != nil { return false, err } + if pool.canAddPkrTx() { + pool.pkrTxOuts.AddPendingTxOut(*tx) + } log.Trace("Pooled new future transaction", "hash", hash, "from", tx.From(), "to", tx.To()) return flag, nil } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 860a3e0b..77372e25 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1262,10 +1262,10 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr return nil, 0, false, err } - if failed { - log.Info("call error", "msg", string(res)) - } - log.Info("result", "data", hexutil.Encode(res)) + //if failed { + // log.Info("call error", "msg", string(res)) + //} + //log.Info("result", "data", hexutil.Encode(res)) return res, gas, failed, err } diff --git a/internal/ethapi/api_light_node.go b/internal/ethapi/api_light_node.go index d65976a8..9c2e3ba4 100644 --- a/internal/ethapi/api_light_node.go +++ b/internal/ethapi/api_light_node.go @@ -29,6 +29,23 @@ func (plna PublicLightNodeApi) GetOutsByPKr(ctx context.Context, addresses []*Mi return plna.b.GetOutByPKr(pkrs, start, end) } +func (plna PublicLightNodeApi) GetPendingOuts(ctx context.Context, addresses []*PKrAddress) (outBlockResp light.BlockOutResp, e error) { + + pkrs := []c_type.PKr{} + for _, address := range addresses { + addr := *address + if len(addr) == 96 { + var pkr c_type.PKr + copy(pkr[:], addr[:]) + pkrs = append(pkrs, pkr) + } else { + return outBlockResp, fmt.Errorf("address is invalid") + } + } + return light.Current_light.GetPendingOuts(pkrs) + +} + func (plna PublicLightNodeApi) CheckNil(Nils []c_type.Uint256) (nilResps []light.NilValue, e error) { return plna.b.CheckNil(Nils) diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 6af5df39..0ffe98c5 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -950,6 +950,11 @@ web3._extend({ call: 'light_getOutsByPKr', params: 3 }), + new web3._extend.Method({ + name: 'getPendingOuts', + call: 'light_getPendingOuts', + params: 1 + }), new web3._extend.Method({ name: 'checkNil', call: 'light_checkNil', diff --git a/sero/api_backend.go b/sero/api_backend.go index dc3abc7f..fbf7081e 100644 --- a/sero/api_backend.go +++ b/sero/api_backend.go @@ -20,6 +20,7 @@ import ( "context" "errors" "math/big" + "time" "github.com/sero-cash/go-sero/zero/txtool/flight" @@ -264,6 +265,11 @@ func (b *SeroAPIBackend) GetAnchor(roots []c_type.Uint256) ([]txtool.Witness, er } func (b *SeroAPIBackend) CommitTx(tx *txtool.GTx) error { + + difference := time.Now().Unix() - b.CurrentBlock().Time().Int64() + if difference > 10*60 { + return errors.New("The current chain is too behind") + } gasPrice := big.Int(tx.GasPrice) gas := uint64(tx.Gas) signedTx := types.NewTxWithGTx(gas, &gasPrice, &tx.Tx) diff --git a/sero/backend.go b/sero/backend.go index 3a92ad2e..34afafc0 100644 --- a/sero/backend.go +++ b/sero/backend.go @@ -21,12 +21,13 @@ import ( "bytes" "errors" "fmt" - "github.com/sero-cash/go-sero/zero/wallet/stakeservice" "math/big" "runtime" "sync" "sync/atomic" + "github.com/sero-cash/go-sero/zero/wallet/stakeservice" + "github.com/sero-cash/go-czero-import/c_type" "github.com/sero-cash/go-czero-import/superzk" @@ -179,6 +180,9 @@ func New(ctx *node.ServiceContext, config *Config) (*Sero, error) { // if config.TxPool.Journal != "" { // config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal) // } + + config.TxPool.StartLight = config.StartLight + sero.txPool = core.NewTxPool(config.TxPool, sero.chainConfig, sero.blockchain) sero.voter = voter.NewVoter(sero.chainConfig, sero.blockchain, sero) diff --git a/zero/wallet/light/api.go b/zero/wallet/light/api.go index e9f45030..59e06434 100644 --- a/zero/wallet/light/api.go +++ b/zero/wallet/light/api.go @@ -2,6 +2,7 @@ package light import ( "bytes" + "sort" "github.com/sero-cash/go-czero-import/c_type" "github.com/sero-cash/go-sero/log" @@ -9,16 +10,16 @@ import ( "github.com/sero-cash/go-sero/zero/txtool" ) -var current_light *LightNode +var Current_light *LightNode func (self *LightNode) CurrentLight() *LightNode { - return current_light + return Current_light } func (self *LightNode) GetOutsByPKr(pkrs []c_type.PKr, start, end uint64) (br BlockOutResp, e error) { br.CurrentNum = self.getLastNumber() blockOuts := []BlockOut{} - if end == 0 { + if end == 0 { end = br.CurrentNum } for _, pkr := range pkrs { @@ -47,6 +48,32 @@ func (self *LightNode) GetOutsByPKr(pkrs []c_type.PKr, start, end uint64) (br Bl return br, nil } +func (self *LightNode) GetPendingOuts(pkrs []c_type.PKr) (br BlockOutResp, e error) { + blockOuts := []BlockOut{} + + numBlokcDatas := self.CurrentLight().getImmatureTx(pkrs) + + if pendingBlockOuts, ok := numBlokcDatas[0]; ok { + if len(pendingBlockOuts) > 0 { + blockOut := BlockOut{Num: 0, Data: pendingBlockOuts} + blockOuts = append(blockOuts, blockOut) + } + } + + immatureBlokOuts := BlocOuts{} + for k, v := range numBlokcDatas { + if k != 0 { + blockOut := BlockOut{Num: k, Data: v} + immatureBlokOuts = append(immatureBlokOuts, blockOut) + } + + } + sort.Sort(immatureBlokOuts) + blockOuts = append(blockOuts, immatureBlokOuts[:]...) + br.BlockOuts = blockOuts + return br, nil +} + func (self *LightNode) CheckNil(Nils []c_type.Uint256) (nilResps []NilValue, e error) { if len(Nils) == 0 { return @@ -80,5 +107,11 @@ type BlockOut struct { type BlockData struct { TxInfo TxInfo - Out txtool.Out + Out txtool.Out } + +type BlocOuts []BlockOut + +func (s BlocOuts) Len() int { return len(s) } +func (s BlocOuts) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s BlocOuts) Less(i, j int) bool { return s[i].Num > s[j].Num } diff --git a/zero/wallet/light/light.go b/zero/wallet/light/light.go index 205972b3..bf2d1c51 100644 --- a/zero/wallet/light/light.go +++ b/zero/wallet/light/light.go @@ -21,6 +21,7 @@ import ( type LightNode struct { db *serodb.LDBDatabase bcDB serodb.Database + //immatureTx *ImmatureTx txPool *core.TxPool @@ -47,13 +48,15 @@ func NewLightNode(dbPath string, txPool *core.TxPool, bcDB serodb.Database) (lig if err != nil { panic(err) } + //immatureTx := NewImmatureTx(db, txPool) lightNode = &LightNode{ txPool: txPool, sri: flight.SRI_Inst, db: db, bcDB: bcDB, + //immatureTx: immatureTx, } - current_light = lightNode + Current_light = lightNode AddJob("0/10 * * * * ?", lightNode.fetchBlockInfo) @@ -91,10 +94,15 @@ func numKey() []byte { } func (self *LightNode) fetchBlockInfo() { + + //self.immatureTx.fetchBlockInfo() + if txtool.Ref_inst.Bc == nil || !txtool.Ref_inst.Bc.IsValid() { return } + start := self.getLastNumber() + blocks, err := self.sri.GetBlocksInfo(start+1, fetchCount) if err != nil { log.Error("light GetBlocksInfo err:", err.Error()) @@ -118,7 +126,7 @@ func (self *LightNode) fetchBlockInfo() { txHash := common.Hash{} copy(txHash[:], out.State.TxHash[:]) - if (teamReward == txHash) { + if teamReward == txHash { continue } @@ -139,6 +147,7 @@ func (self *LightNode) fetchBlockInfo() { // To: *tx.To(), Time: *blockDB.Time(), } + self.txPool.DelMaturedOuts(*out.State.OS.ToPKr(), out.State.TxHash, blockNum) } else { txInfo = TxInfo{ Num: blockNum, @@ -155,6 +164,9 @@ func (self *LightNode) fetchBlockInfo() { } pkr := *out.State.OS.ToPKr() + + //self.immatureTx.lockedDelImmatureTx(pkr, blockData.TxInfo.TxHash) + if value, ok := pkrMap[pkr]; ok { v := value v = append(v, blockData) @@ -321,3 +333,48 @@ func (r *RunJob) Run() { r.run() } + +func outInfoToTxInfo(info core.TxOutInfo) TxInfo { + + txInfo := TxInfo{ + TxHash: info.TxHash, + Num: info.BlockNumber, + BlockHash: info.BlockHash, + Gas: info.Gas, + GasUsed: info.GasUsed, + GasPrice: *info.GasPrice, + From: info.From, + Time: *big.NewInt(0).SetUint64(info.Time), + } + return txInfo +} + +func (self *LightNode) getImmatureTx(pkrs []c_type.PKr) (immatureBlockOuts map[uint64][]BlockData) { + + immatureBlockOuts = make(map[uint64][]BlockData) + + lastLightNum := self.getLastNumber() + + for _, pkr := range pkrs { + + txPoolTxOut := self.txPool.PendingOuts(pkr) + + for _, outInfo := range txPoolTxOut { + + if outInfo != nil { + txInfo := outInfoToTxInfo(*outInfo) + for index := range outInfo.Outs { + blockData := BlockData{ + TxInfo: txInfo, + Out: outInfo.Outs[index], + } + if blockData.TxInfo.Num == 0 || (blockData.TxInfo.Num+seroparam.DefaultConfirmedBlock()) > lastLightNum { + immatureBlockOuts[blockData.TxInfo.Num] = append(immatureBlockOuts[blockData.TxInfo.Num], blockData) + } + } + } + } + } + return + +}