From 4449c8f1b3d4a55d22004d09a735f518c7c0b862 Mon Sep 17 00:00:00 2001 From: Yongfeng LI Date: Sat, 24 Mar 2018 16:19:44 +0800 Subject: [PATCH] extract api from blockchain (#478) --- {blockchain => api}/accesstokens.go | 2 +- {blockchain => api}/accounts.go | 2 +- {blockchain => api}/api.go | 14 +-- {blockchain => api}/assets.go | 2 +- api/blockchain_reactor.go | 185 +++++++++++++++++++++++++++++++++ {blockchain => api}/errors.go | 2 +- {blockchain => api}/hsm.go | 2 +- {blockchain => api}/metrics.go | 2 +- api/miner.go | 34 ++++++ {blockchain => api}/query.go | 2 +- {blockchain => api}/receivers.go | 2 +- {blockchain => api}/request.go | 2 +- api/transact.go | 187 +++++++++++++++++++++++++++++++++ {blockchain => api}/transact_test.go | 2 +- api/txfeeds.go | 92 ++++++++++++++++ api/types.go | 16 --- {blockchain => api}/wallet.go | 2 +- blockchain/blockchain_reactor.go | 196 ++--------------------------------- blockchain/miner.go | 41 ++------ blockchain/transact.go | 176 ------------------------------- blockchain/txfeeds.go | 87 +--------------- cmd/bytomcli/commands/key.go | 4 +- cmd/bytomcli/commands/transaction.go | 4 +- cmd/miner/main.go | 4 +- node/node.go | 11 +- util/util.go | 5 +- 26 files changed, 553 insertions(+), 525 deletions(-) rename {blockchain => api}/accesstokens.go (98%) rename {blockchain => api}/accounts.go (99%) rename {blockchain => api}/api.go (96%) rename {blockchain => api}/assets.go (98%) create mode 100644 api/blockchain_reactor.go rename {blockchain => api}/errors.go (99%) rename {blockchain => api}/hsm.go (99%) rename {blockchain => api}/metrics.go (98%) create mode 100644 api/miner.go rename {blockchain => api}/query.go (99%) rename {blockchain => api}/receivers.go (97%) rename {blockchain => api}/request.go (98%) create mode 100644 api/transact.go rename {blockchain => api}/transact_test.go (99%) create mode 100644 api/txfeeds.go rename {blockchain => api}/wallet.go (98%) diff --git a/blockchain/accesstokens.go b/api/accesstokens.go similarity index 98% rename from blockchain/accesstokens.go rename to api/accesstokens.go index 7ca971f4..3754e096 100644 --- a/blockchain/accesstokens.go +++ b/api/accesstokens.go @@ -1,4 +1,4 @@ -package blockchain +package api import ( "context" diff --git a/blockchain/accounts.go b/api/accounts.go similarity index 99% rename from blockchain/accounts.go rename to api/accounts.go index 42269d8a..071fe0e9 100644 --- a/blockchain/accounts.go +++ b/api/accounts.go @@ -1,4 +1,4 @@ -package blockchain +package api import ( "context" diff --git a/blockchain/api.go b/api/api.go similarity index 96% rename from blockchain/api.go rename to api/api.go index 4da6e4e1..2ddd718c 100644 --- a/blockchain/api.go +++ b/api/api.go @@ -1,4 +1,4 @@ -package blockchain +package api import ( "crypto/tls" @@ -12,6 +12,7 @@ import ( cmn "github.com/tendermint/tmlibs/common" "github.com/bytom/accesstoken" + "github.com/bytom/blockchain" cfg "github.com/bytom/config" "github.com/bytom/dashboard" "github.com/bytom/errors" @@ -32,7 +33,8 @@ const ( // SUCCESS indicates the rpc calling is successful. SUCCESS = "success" // FAIL indicated the rpc calling is failed. - FAIL = "fail" + FAIL = "fail" + crosscoreRPCPrefix = "/rpc/" ) // Response describes the response standard. @@ -68,7 +70,7 @@ func (wh *waitHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } type API struct { - bcr *BlockchainReactor + bcr *blockchain.BlockchainReactor wallet *wallet.Wallet chain *protocol.Chain server *http.Server @@ -126,11 +128,11 @@ func (a *API) StartServer(address string) { }() } -func NewAPI(bcr *BlockchainReactor, config *cfg.Config) *API { +func NewAPI(bcr *blockchain.BlockchainReactor, wallet *wallet.Wallet, chain *protocol.Chain, config *cfg.Config) *API { api := &API{ bcr: bcr, - wallet: bcr.wallet, - chain: bcr.chain, + wallet: wallet, + chain: chain, } api.buildHandler() api.initServer(config) diff --git a/blockchain/assets.go b/api/assets.go similarity index 98% rename from blockchain/assets.go rename to api/assets.go index e16480ec..ff18e844 100644 --- a/blockchain/assets.go +++ b/api/assets.go @@ -1,4 +1,4 @@ -package blockchain +package api import ( "context" diff --git a/api/blockchain_reactor.go b/api/blockchain_reactor.go new file mode 100644 index 00000000..20972a96 --- /dev/null +++ b/api/blockchain_reactor.go @@ -0,0 +1,185 @@ +package api + +import ( + log "github.com/sirupsen/logrus" + + "github.com/bytom/blockchain/query" + "github.com/bytom/wallet" + "github.com/bytom/consensus" + "github.com/bytom/consensus/difficulty" + chainjson "github.com/bytom/encoding/json" + "github.com/bytom/protocol/bc" + "github.com/bytom/protocol/bc/types" +) + +// return network infomation +func (a *API) getNetInfo() Response { + return NewSuccessResponse(a.bcr.GetNodeInfo()) +} + +// return best block hash +func (a *API) getBestBlockHash() Response { + blockHash := map[string]string{"blockHash": a.chain.BestBlockHash().String()} + return NewSuccessResponse(blockHash) +} + +// return block header by hash +func (a *API) getBlockHeaderByHash(strHash string) Response { + hash := bc.Hash{} + if err := hash.UnmarshalText([]byte(strHash)); err != nil { + log.WithField("error", err).Error("Error occurs when transforming string hash to hash struct") + return NewErrorResponse(err) + } + block, err := a.chain.GetBlockByHash(&hash) + if err != nil { + log.WithField("error", err).Error("Fail to get block by hash") + return NewErrorResponse(err) + } + + bcBlock := types.MapBlock(block) + return NewSuccessResponse(bcBlock.BlockHeader) +} + +// BlockTx is the tx struct for getBlock func +type BlockTx struct { + ID bc.Hash `json:"id"` + Version uint64 `json:"version"` + Size uint64 `json:"size"` + TimeRange uint64 `json:"time_range"` + Inputs []*query.AnnotatedInput `json:"inputs"` + Outputs []*query.AnnotatedOutput `json:"outputs"` + StatusFail bool `json:"status_fail"` +} + +// GetBlockReq is used to handle getBlock req +type GetBlockReq struct { + BlockHeight uint64 `json:"block_height"` + BlockHash chainjson.HexBytes `json:"block_hash"` +} + +// GetBlockResp is the resp for getBlock api +type GetBlockResp struct { + Hash *bc.Hash `json:"hash"` + Size uint64 `json:"size"` + Version uint64 `json:"version"` + Height uint64 `json:"height"` + PreviousBlockHash *bc.Hash `json:"previous_block_hash"` + Timestamp uint64 `json:"timestamp"` + Nonce uint64 `json:"nonce"` + Bits uint64 `json:"bits"` + Difficulty string `json:"difficulty"` + TransactionsMerkleRoot *bc.Hash `json:"transaction_merkle_root"` + TransactionStatusHash *bc.Hash `json:"transaction_status_hash"` + Transactions []*BlockTx `json:"transactions"` +} + +// return block by hash +func (a *API) getBlock(ins GetBlockReq) Response { + var err error + block := &types.Block{} + if len(ins.BlockHash) == 32 { + b32 := [32]byte{} + copy(b32[:], ins.BlockHash) + hash := bc.NewHash(b32) + block, err = a.chain.GetBlockByHash(&hash) + } else { + block, err = a.chain.GetBlockByHeight(ins.BlockHeight) + } + if err != nil { + return NewErrorResponse(err) + } + + blockHash := block.Hash() + txStatus, err := a.chain.GetTransactionStatus(&blockHash) + rawBlock, err := block.MarshalText() + if err != nil { + return NewErrorResponse(err) + } + + resp := &GetBlockResp{ + Hash: &blockHash, + Size: uint64(len(rawBlock)), + Version: block.Version, + Height: block.Height, + PreviousBlockHash: &block.PreviousBlockHash, + Timestamp: block.Timestamp, + Nonce: block.Nonce, + Bits: block.Bits, + Difficulty: difficulty.CompactToBig(block.Bits).String(), + TransactionsMerkleRoot: &block.TransactionsMerkleRoot, + TransactionStatusHash: &block.TransactionStatusHash, + Transactions: []*BlockTx{}, + } + + for i, orig := range block.Transactions { + tx := &BlockTx{ + ID: orig.ID, + Version: orig.Version, + Size: orig.SerializedSize, + TimeRange: orig.TimeRange, + Inputs: []*query.AnnotatedInput{}, + Outputs: []*query.AnnotatedOutput{}, + } + tx.StatusFail, err = txStatus.GetStatus(i) + if err != nil { + NewSuccessResponse(resp) + } + + for i := range orig.Inputs { + tx.Inputs = append(tx.Inputs, wallet.BuildAnnotatedInput(orig, uint32(i))) + } + for i := range orig.Outputs { + tx.Outputs = append(tx.Outputs, wallet.BuildAnnotatedOutput(orig, i)) + } + resp.Transactions = append(resp.Transactions, tx) + } + return NewSuccessResponse(resp) +} + +// return block transactions count by hash +func (a *API) getBlockTransactionsCountByHash(strHash string) Response { + hash := bc.Hash{} + if err := hash.UnmarshalText([]byte(strHash)); err != nil { + log.WithField("error", err).Error("Error occurs when transforming string hash to hash struct") + return NewErrorResponse(err) + } + + legacyBlock, err := a.chain.GetBlockByHash(&hash) + if err != nil { + log.WithField("error", err).Error("Fail to get block by hash") + return NewErrorResponse(err) + } + + count := map[string]int{"count": len(legacyBlock.Transactions)} + return NewSuccessResponse(count) +} + +// return block transactions count by height +func (a *API) getBlockTransactionsCountByHeight(height uint64) Response { + legacyBlock, err := a.chain.GetBlockByHeight(height) + if err != nil { + log.WithField("error", err).Error("Fail to get block by hash") + return NewErrorResponse(err) + } + + count := map[string]int{"count": len(legacyBlock.Transactions)} + return NewSuccessResponse(count) +} + +// return current block count +func (a *API) getBlockCount() Response { + blockHeight := map[string]uint64{"block_count": a.chain.Height()} + return NewSuccessResponse(blockHeight) +} + +// return is in mining or not +func (a *API) isMining() Response { + IsMining := map[string]bool{"isMining": a.bcr.IsMining()} + return NewSuccessResponse(IsMining) +} + +// return gasRate +func (a *API) gasRate() Response { + gasrate := map[string]int64{"gasRate": consensus.VMGasRate} + return NewSuccessResponse(gasrate) +} diff --git a/blockchain/errors.go b/api/errors.go similarity index 99% rename from blockchain/errors.go rename to api/errors.go index 1a0909c9..9d617a1c 100644 --- a/blockchain/errors.go +++ b/api/errors.go @@ -1,4 +1,4 @@ -package blockchain +package api import ( "context" diff --git a/blockchain/hsm.go b/api/hsm.go similarity index 99% rename from blockchain/hsm.go rename to api/hsm.go index 36635af4..694073e9 100644 --- a/blockchain/hsm.go +++ b/api/hsm.go @@ -1,4 +1,4 @@ -package blockchain +package api import ( "context" diff --git a/blockchain/metrics.go b/api/metrics.go similarity index 98% rename from blockchain/metrics.go rename to api/metrics.go index 202364cf..3ead8a71 100644 --- a/blockchain/metrics.go +++ b/api/metrics.go @@ -1,4 +1,4 @@ -package blockchain +package api import ( "expvar" diff --git a/api/miner.go b/api/miner.go new file mode 100644 index 00000000..9bafba44 --- /dev/null +++ b/api/miner.go @@ -0,0 +1,34 @@ +package api + +import ( + "context" + + "github.com/bytom/protocol/bc/types" +) + +func (a *API) getWork() Response { + work, err := a.bcr.GetWork() + if err != nil { + return NewErrorResponse(err) + } + return NewSuccessResponse(work) +} + +func (a *API) submitWork(bh *types.BlockHeader) Response { + return NewSuccessResponse(a.bcr.SubmitWork(bh)) +} + +func (a *API) getBlockHeaderByHeight(ctx context.Context, req struct { + Height uint64 `json:"block_height"` +}) Response { + block, err := a.chain.GetBlockByHeight(req.Height) + if err != nil { + return NewErrorResponse(err) + } + + resp := &BlockHeaderByHeight{ + BlockHeader: &block.BlockHeader, + Reward: block.Transactions[0].Outputs[0].Amount, + } + return NewSuccessResponse(resp) +} diff --git a/blockchain/query.go b/api/query.go similarity index 99% rename from blockchain/query.go rename to api/query.go index 364da93f..defcaeef 100755 --- a/blockchain/query.go +++ b/api/query.go @@ -1,4 +1,4 @@ -package blockchain +package api import ( "context" diff --git a/blockchain/receivers.go b/api/receivers.go similarity index 97% rename from blockchain/receivers.go rename to api/receivers.go index 04c8f5c5..a7dfcab0 100755 --- a/blockchain/receivers.go +++ b/api/receivers.go @@ -1,4 +1,4 @@ -package blockchain +package api import ( "context" diff --git a/blockchain/request.go b/api/request.go similarity index 98% rename from blockchain/request.go rename to api/request.go index 843a3810..60980b6b 100644 --- a/blockchain/request.go +++ b/api/request.go @@ -1,4 +1,4 @@ -package blockchain +package api import ( "context" diff --git a/api/transact.go b/api/transact.go new file mode 100644 index 00000000..58b96524 --- /dev/null +++ b/api/transact.go @@ -0,0 +1,187 @@ +package api + +import ( + "context" + "encoding/json" + "fmt" + "time" + + log "github.com/sirupsen/logrus" + + "github.com/bytom/blockchain/txbuilder" + "github.com/bytom/errors" + "github.com/bytom/net/http/reqid" + "github.com/bytom/protocol/bc" + "github.com/bytom/protocol/bc/types" +) + +var defaultTxTTL = 5 * time.Minute + +func (a *API) actionDecoder(action string) (func([]byte) (txbuilder.Action, error), bool) { + var decoder func([]byte) (txbuilder.Action, error) + switch action { + case "control_account": + decoder = a.wallet.AccountMgr.DecodeControlAction + case "control_address": + decoder = txbuilder.DecodeControlAddressAction + case "control_program": + decoder = txbuilder.DecodeControlProgramAction + case "control_receiver": + decoder = txbuilder.DecodeControlReceiverAction + case "issue": + decoder = a.wallet.AssetReg.DecodeIssueAction + case "retire": + decoder = txbuilder.DecodeRetireAction + case "spend_account": + decoder = a.wallet.AccountMgr.DecodeSpendAction + case "spend_account_unspent_output": + decoder = a.wallet.AccountMgr.DecodeSpendUTXOAction + default: + return nil, false + } + return decoder, true +} + +func mergeActions(req *BuildRequest) []map[string]interface{} { + actions := make([]map[string]interface{}, 0) + actionMap := make(map[string]map[string]interface{}) + + for _, m := range req.Actions { + if actionType := m["type"].(string); actionType != "spend_account" { + actions = append(actions, m) + continue + } + + actionKey := m["asset_id"].(string) + m["account_id"].(string) + amountNumber := m["amount"].(json.Number) + amount, _ := amountNumber.Int64() + + if tmpM, ok := actionMap[actionKey]; ok { + tmpNumber, _ := tmpM["amount"].(json.Number) + tmpAmount, _ := tmpNumber.Int64() + tmpM["amount"] = json.Number(fmt.Sprintf("%v", tmpAmount+amount)) + } else { + actionMap[actionKey] = m + actions = append(actions, m) + } + } + + return actions +} + +func (a *API) buildSingle(ctx context.Context, req *BuildRequest) (*txbuilder.Template, error) { + err := a.filterAliases(ctx, req) + if err != nil { + return nil, err + } + reqActions := mergeActions(req) + actions := make([]txbuilder.Action, 0, len(reqActions)) + for i, act := range reqActions { + typ, ok := act["type"].(string) + if !ok { + return nil, errors.WithDetailf(errBadActionType, "no action type provided on action %d", i) + } + decoder, ok := a.actionDecoder(typ) + if !ok { + return nil, errors.WithDetailf(errBadActionType, "unknown action type %q on action %d", typ, i) + } + + // Remarshal to JSON, the action may have been modified when we + // filtered aliases. + b, err := json.Marshal(act) + if err != nil { + return nil, err + } + action, err := decoder(b) + if err != nil { + return nil, errors.WithDetailf(errBadAction, "%s on action %d", err.Error(), i) + } + actions = append(actions, action) + } + + ttl := req.TTL.Duration + if ttl == 0 { + ttl = defaultTxTTL + } + maxTime := time.Now().Add(ttl) + + tpl, err := txbuilder.Build(ctx, req.Tx, actions, maxTime, req.TimeRange) + if errors.Root(err) == txbuilder.ErrAction { + // append each of the inner errors contained in the data. + var Errs string + for _, innerErr := range errors.Data(err)["actions"].([]error) { + Errs = Errs + "<" + innerErr.Error() + ">" + } + err = errors.New(err.Error() + "-" + Errs) + } + if err != nil { + return nil, err + } + + // ensure null is never returned for signing instructions + if tpl.SigningInstructions == nil { + tpl.SigningInstructions = []*txbuilder.SigningInstruction{} + } + return tpl, nil +} + +// POST /build-transaction +func (a *API) build(ctx context.Context, buildReqs *BuildRequest) Response { + subctx := reqid.NewSubContext(ctx, reqid.New()) + + tmpl, err := a.buildSingle(subctx, buildReqs) + if err != nil { + return NewErrorResponse(err) + } + + return NewSuccessResponse(tmpl) +} + +func (a *API) submitSingle(ctx context.Context, tpl *txbuilder.Template) (map[string]string, error) { + if tpl.Transaction == nil { + return nil, errors.Wrap(txbuilder.ErrMissingRawTx) + } + + if err := txbuilder.FinalizeTx(ctx, a.chain, tpl.Transaction); err != nil { + return nil, errors.Wrapf(err, "tx %s", tpl.Transaction.ID.String()) + } + + return map[string]string{"tx_id": tpl.Transaction.ID.String()}, nil +} + +type submitTxResp struct { + TxID *bc.Hash `json:"tx_id"` +} + +// POST /submit-transaction +func (a *API) submit(ctx context.Context, ins struct { + Tx types.Tx `json:"raw_transaction"` +}) Response { + if err := txbuilder.FinalizeTx(ctx, a.chain, &ins.Tx); err != nil { + return NewErrorResponse(err) + } + + log.WithField("tx_id", ins.Tx.ID).Info("submit single tx") + return NewSuccessResponse(&submitTxResp{TxID: &ins.Tx.ID}) +} + +// POST /sign-submit-transaction +func (a *API) signSubmit(ctx context.Context, x struct { + Password []string `json:"password"` + Txs txbuilder.Template `json:"transaction"` +}) Response { + if err := txbuilder.Sign(ctx, &x.Txs, nil, x.Password[0], a.pseudohsmSignTemplate); err != nil { + log.WithField("build err", err).Error("fail on sign transaction.") + return NewErrorResponse(err) + } + log.Info("Sign Transaction complete.") + + txID, err := a.submitSingle(nil, &x.Txs) + if err != nil { + log.WithField("err", err).Error("submit single tx") + return NewErrorResponse(err) + } + + log.WithField("tx_id", txID["tx_id"]).Info("submit single tx") + return NewSuccessResponse(txID) +} diff --git a/blockchain/transact_test.go b/api/transact_test.go similarity index 99% rename from blockchain/transact_test.go rename to api/transact_test.go index dfd6bd6f..0b40079b 100644 --- a/blockchain/transact_test.go +++ b/api/transact_test.go @@ -1,4 +1,4 @@ -package blockchain +package api import ( "encoding/json" diff --git a/api/txfeeds.go b/api/txfeeds.go new file mode 100644 index 00000000..c1153367 --- /dev/null +++ b/api/txfeeds.go @@ -0,0 +1,92 @@ +package api + +import ( + "context" + "encoding/json" + + log "github.com/sirupsen/logrus" + + "github.com/bytom/blockchain/txfeed" +) + +// POST /create-txfeed +func (a *API) createTxFeed(ctx context.Context, in struct { + Alias string `json:"alias"` + Filter string `json:"filter"` +}) Response { + if err := a.bcr.TxFeedTracker.Create(ctx, in.Alias, in.Filter); err != nil { + log.WithField("error", err).Error("Add TxFeed Failed") + return NewErrorResponse(err) + } + return NewSuccessResponse(nil) +} + +// POST /get-transaction-feed +func (a *API) getTxFeed(ctx context.Context, in struct { + Alias string `json:"alias,omitempty"` +}) Response { + var tmpTxFeed interface{} + rawTxfeed, err := a.bcr.GetTxFeedByAlias(ctx, in.Alias) + if err != nil { + return NewErrorResponse(err) + } + err = json.Unmarshal(rawTxfeed, &tmpTxFeed) + if err != nil { + return NewErrorResponse(err) + } + data := map[string]interface{}{"txfeed": tmpTxFeed} + return NewSuccessResponse(data) +} + +// POST /delete-transaction-feed +func (a *API) deleteTxFeed(ctx context.Context, in struct { + Alias string `json:"alias,omitempty"` +}) Response { + if err := a.bcr.TxFeedTracker.Delete(ctx, in.Alias); err != nil { + return NewErrorResponse(err) + } + return NewSuccessResponse(nil) +} + +// POST /update-transaction-feed +func (a *API) updateTxFeed(ctx context.Context, in struct { + Alias string `json:"alias"` + Filter string `json:"filter"` +}) Response { + if err := a.bcr.TxFeedTracker.Delete(ctx, in.Alias); err != nil { + return NewErrorResponse(err) + } + if err := a.bcr.TxFeedTracker.Create(ctx, in.Alias, in.Filter); err != nil { + log.WithField("error", err).Error("Update TxFeed Failed") + return NewErrorResponse(err) + } + return NewSuccessResponse(nil) +} + +func (a *API) getTxFeeds() ([]txfeed.TxFeed, error) { + txFeed := txfeed.TxFeed{} + txFeeds := make([]txfeed.TxFeed, 0) + + iter := a.bcr.TxFeedTracker.DB.Iterator() + defer iter.Release() + + for iter.Next() { + if err := json.Unmarshal(iter.Value(), &txFeed); err != nil { + return nil, err + } + txFeeds = append(txFeeds, txFeed) + } + + return txFeeds, nil +} + +// listTxFeeds is an http handler for listing txfeeds. It does not take a filter. +// POST /list-transaction-feeds +func (a *API) listTxFeeds(ctx context.Context) Response { + txFeeds, err := a.getTxFeeds() + if err != nil { + return NewErrorResponse(err) + } + + return NewSuccessResponse(txFeeds) +} diff --git a/api/types.go b/api/types.go index b7a52188..72f6acee 100644 --- a/api/types.go +++ b/api/types.go @@ -1,7 +1,6 @@ package api import ( - "github.com/bytom/protocol/bc" "github.com/bytom/protocol/bc/types" ) @@ -10,18 +9,3 @@ type BlockHeaderByHeight struct { BlockHeader *types.BlockHeader `json:"block_header"` Reward uint64 `json:"reward"` } - -// GetWorkResp is resp struct for API -type GetWorkResp struct { - BlockHeader *types.BlockHeader `json:"block_header"` - Seed *bc.Hash `json:"seed"` -} - -type NetInfo struct { - Listening bool `json:"listening"` - Syncing bool `json:"syncing"` - Mining bool `json:"mining"` - PeerCount int `json:"peer_count"` - CurrentBlock uint64 `json:"current_block"` - HighestBlock uint64 `json:"highest_block"` -} diff --git a/blockchain/wallet.go b/api/wallet.go similarity index 98% rename from blockchain/wallet.go rename to api/wallet.go index faee8167..15191705 100644 --- a/blockchain/wallet.go +++ b/api/wallet.go @@ -1,4 +1,4 @@ -package blockchain +package api import ( "bytes" diff --git a/blockchain/blockchain_reactor.go b/blockchain/blockchain_reactor.go index 4108efe4..19f9dda5 100644 --- a/blockchain/blockchain_reactor.go +++ b/blockchain/blockchain_reactor.go @@ -1,20 +1,16 @@ package blockchain -import ( - log "github.com/sirupsen/logrus" - - "github.com/bytom/api" - "github.com/bytom/blockchain/query" - "github.com/bytom/wallet" - "github.com/bytom/consensus" - "github.com/bytom/consensus/difficulty" - chainjson "github.com/bytom/encoding/json" - "github.com/bytom/protocol/bc" - "github.com/bytom/protocol/bc/types" -) +type NetInfo struct { + Listening bool `json:"listening"` + Syncing bool `json:"syncing"` + Mining bool `json:"mining"` + PeerCount int `json:"peer_count"` + CurrentBlock uint64 `json:"current_block"` + HighestBlock uint64 `json:"highest_block"` +} -func (bcr *BlockchainReactor) GetNodeInfo() *api.NetInfo { - return &api.NetInfo{ +func (bcr *BlockchainReactor) GetNodeInfo() *NetInfo { + return &NetInfo{ Listening: bcr.sw.IsListening(), Syncing: bcr.blockKeeper.IsCaughtUp(), Mining: bcr.mining.IsMining(), @@ -27,175 +23,3 @@ func (bcr *BlockchainReactor) GetNodeInfo() *api.NetInfo { func (bcr *BlockchainReactor) IsMining() bool { return bcr.mining.IsMining() } - -// return network infomation -func (a *API) getNetInfo() Response { - return NewSuccessResponse(a.bcr.GetNodeInfo()) -} - -// return best block hash -func (a *API) getBestBlockHash() Response { - blockHash := map[string]string{"blockHash": a.chain.BestBlockHash().String()} - return NewSuccessResponse(blockHash) -} - -// return block header by hash -func (a *API) getBlockHeaderByHash(strHash string) Response { - hash := bc.Hash{} - if err := hash.UnmarshalText([]byte(strHash)); err != nil { - log.WithField("error", err).Error("Error occurs when transforming string hash to hash struct") - return NewErrorResponse(err) - } - block, err := a.chain.GetBlockByHash(&hash) - if err != nil { - log.WithField("error", err).Error("Fail to get block by hash") - return NewErrorResponse(err) - } - - bcBlock := types.MapBlock(block) - return NewSuccessResponse(bcBlock.BlockHeader) -} - -// BlockTx is the tx struct for getBlock func -type BlockTx struct { - ID bc.Hash `json:"id"` - Version uint64 `json:"version"` - Size uint64 `json:"size"` - TimeRange uint64 `json:"time_range"` - Inputs []*query.AnnotatedInput `json:"inputs"` - Outputs []*query.AnnotatedOutput `json:"outputs"` - StatusFail bool `json:"status_fail"` -} - -// GetBlockReq is used to handle getBlock req -type GetBlockReq struct { - BlockHeight uint64 `json:"block_height"` - BlockHash chainjson.HexBytes `json:"block_hash"` -} - -// GetBlockResp is the resp for getBlock api -type GetBlockResp struct { - Hash *bc.Hash `json:"hash"` - Size uint64 `json:"size"` - Version uint64 `json:"version"` - Height uint64 `json:"height"` - PreviousBlockHash *bc.Hash `json:"previous_block_hash"` - Timestamp uint64 `json:"timestamp"` - Nonce uint64 `json:"nonce"` - Bits uint64 `json:"bits"` - Difficulty string `json:"difficulty"` - TransactionsMerkleRoot *bc.Hash `json:"transaction_merkle_root"` - TransactionStatusHash *bc.Hash `json:"transaction_status_hash"` - Transactions []*BlockTx `json:"transactions"` -} - -// return block by hash -func (a *API) getBlock(ins GetBlockReq) Response { - var err error - block := &types.Block{} - if len(ins.BlockHash) == 32 { - b32 := [32]byte{} - copy(b32[:], ins.BlockHash) - hash := bc.NewHash(b32) - block, err = a.chain.GetBlockByHash(&hash) - } else { - block, err = a.chain.GetBlockByHeight(ins.BlockHeight) - } - if err != nil { - return NewErrorResponse(err) - } - - blockHash := block.Hash() - txStatus, err := a.chain.GetTransactionStatus(&blockHash) - rawBlock, err := block.MarshalText() - if err != nil { - return NewErrorResponse(err) - } - - resp := &GetBlockResp{ - Hash: &blockHash, - Size: uint64(len(rawBlock)), - Version: block.Version, - Height: block.Height, - PreviousBlockHash: &block.PreviousBlockHash, - Timestamp: block.Timestamp, - Nonce: block.Nonce, - Bits: block.Bits, - Difficulty: difficulty.CompactToBig(block.Bits).String(), - TransactionsMerkleRoot: &block.TransactionsMerkleRoot, - TransactionStatusHash: &block.TransactionStatusHash, - Transactions: []*BlockTx{}, - } - - for i, orig := range block.Transactions { - tx := &BlockTx{ - ID: orig.ID, - Version: orig.Version, - Size: orig.SerializedSize, - TimeRange: orig.TimeRange, - Inputs: []*query.AnnotatedInput{}, - Outputs: []*query.AnnotatedOutput{}, - } - tx.StatusFail, err = txStatus.GetStatus(i) - if err != nil { - NewSuccessResponse(resp) - } - - for i := range orig.Inputs { - tx.Inputs = append(tx.Inputs, wallet.BuildAnnotatedInput(orig, uint32(i))) - } - for i := range orig.Outputs { - tx.Outputs = append(tx.Outputs, wallet.BuildAnnotatedOutput(orig, i)) - } - resp.Transactions = append(resp.Transactions, tx) - } - return NewSuccessResponse(resp) -} - -// return block transactions count by hash -func (a *API) getBlockTransactionsCountByHash(strHash string) Response { - hash := bc.Hash{} - if err := hash.UnmarshalText([]byte(strHash)); err != nil { - log.WithField("error", err).Error("Error occurs when transforming string hash to hash struct") - return NewErrorResponse(err) - } - - legacyBlock, err := a.chain.GetBlockByHash(&hash) - if err != nil { - log.WithField("error", err).Error("Fail to get block by hash") - return NewErrorResponse(err) - } - - count := map[string]int{"count": len(legacyBlock.Transactions)} - return NewSuccessResponse(count) -} - -// return block transactions count by height -func (a *API) getBlockTransactionsCountByHeight(height uint64) Response { - legacyBlock, err := a.chain.GetBlockByHeight(height) - if err != nil { - log.WithField("error", err).Error("Fail to get block by hash") - return NewErrorResponse(err) - } - - count := map[string]int{"count": len(legacyBlock.Transactions)} - return NewSuccessResponse(count) -} - -// return current block count -func (a *API) getBlockCount() Response { - blockHeight := map[string]uint64{"block_count": a.chain.Height()} - return NewSuccessResponse(blockHeight) -} - -// return is in mining or not -func (a *API) isMining() Response { - IsMining := map[string]bool{"isMining": a.bcr.IsMining()} - return NewSuccessResponse(IsMining) -} - -// return gasRate -func (a *API) gasRate() Response { - gasrate := map[string]int64{"gasRate": consensus.VMGasRate} - return NewSuccessResponse(gasrate) -} diff --git a/blockchain/miner.go b/blockchain/miner.go index a8d2c5d7..4f7bea02 100644 --- a/blockchain/miner.go +++ b/blockchain/miner.go @@ -1,13 +1,17 @@ package blockchain import ( - "context" - - "github.com/bytom/api" + "github.com/bytom/protocol/bc" "github.com/bytom/protocol/bc/types" ) -func (bcr *BlockchainReactor) GetWork() (*api.GetWorkResp, error) { +// GetWorkResp is resp struct for API +type GetWorkResp struct { + BlockHeader *types.BlockHeader `json:"block_header"` + Seed *bc.Hash `json:"seed"` +} + +func (bcr *BlockchainReactor) GetWork() (*GetWorkResp, error) { bh, err := bcr.miningPool.GetWork() if err != nil { return nil, err @@ -18,7 +22,7 @@ func (bcr *BlockchainReactor) GetWork() (*api.GetWorkResp, error) { return nil, err } - return &api.GetWorkResp{ + return &GetWorkResp{ BlockHeader: bh, Seed: seed, }, nil @@ -27,30 +31,3 @@ func (bcr *BlockchainReactor) GetWork() (*api.GetWorkResp, error) { func (bcr *BlockchainReactor) SubmitWork(bh *types.BlockHeader) bool { return bcr.miningPool.SubmitWork(bh) } - -func (a *API) getWork() Response { - work, err := a.bcr.GetWork() - if err != nil { - return NewErrorResponse(err) - } - return NewSuccessResponse(work) -} - -func (a *API) submitWork(bh *types.BlockHeader) Response { - return NewSuccessResponse(a.bcr.SubmitWork(bh)) -} - -func (a *API) getBlockHeaderByHeight(ctx context.Context, req struct { - Height uint64 `json:"block_height"` -}) Response { - block, err := a.chain.GetBlockByHeight(req.Height) - if err != nil { - return NewErrorResponse(err) - } - - resp := &api.BlockHeaderByHeight{ - BlockHeader: &block.BlockHeader, - Reward: block.Transactions[0].Outputs[0].Amount, - } - return NewSuccessResponse(resp) -} diff --git a/blockchain/transact.go b/blockchain/transact.go index ae41457c..4a254aa5 100644 --- a/blockchain/transact.go +++ b/blockchain/transact.go @@ -2,153 +2,14 @@ package blockchain import ( "context" - "encoding/json" - "fmt" - "time" log "github.com/sirupsen/logrus" "github.com/bytom/blockchain/txbuilder" "github.com/bytom/errors" - "github.com/bytom/net/http/reqid" - "github.com/bytom/protocol/bc" "github.com/bytom/protocol/bc/types" ) -var defaultTxTTL = 5 * time.Minute - -func (a *API) actionDecoder(action string) (func([]byte) (txbuilder.Action, error), bool) { - var decoder func([]byte) (txbuilder.Action, error) - switch action { - case "control_account": - decoder = a.wallet.AccountMgr.DecodeControlAction - case "control_address": - decoder = txbuilder.DecodeControlAddressAction - case "control_program": - decoder = txbuilder.DecodeControlProgramAction - case "control_receiver": - decoder = txbuilder.DecodeControlReceiverAction - case "issue": - decoder = a.wallet.AssetReg.DecodeIssueAction - case "retire": - decoder = txbuilder.DecodeRetireAction - case "spend_account": - decoder = a.wallet.AccountMgr.DecodeSpendAction - case "spend_account_unspent_output": - decoder = a.wallet.AccountMgr.DecodeSpendUTXOAction - default: - return nil, false - } - return decoder, true -} - -func mergeActions(req *BuildRequest) []map[string]interface{} { - actions := make([]map[string]interface{}, 0) - actionMap := make(map[string]map[string]interface{}) - - for _, m := range req.Actions { - if actionType := m["type"].(string); actionType != "spend_account" { - actions = append(actions, m) - continue - } - - actionKey := m["asset_id"].(string) + m["account_id"].(string) - amountNumber := m["amount"].(json.Number) - amount, _ := amountNumber.Int64() - - if tmpM, ok := actionMap[actionKey]; ok { - tmpNumber, _ := tmpM["amount"].(json.Number) - tmpAmount, _ := tmpNumber.Int64() - tmpM["amount"] = json.Number(fmt.Sprintf("%v", tmpAmount+amount)) - } else { - actionMap[actionKey] = m - actions = append(actions, m) - } - } - - return actions -} - -func (a *API) buildSingle(ctx context.Context, req *BuildRequest) (*txbuilder.Template, error) { - err := a.filterAliases(ctx, req) - if err != nil { - return nil, err - } - reqActions := mergeActions(req) - actions := make([]txbuilder.Action, 0, len(reqActions)) - for i, act := range reqActions { - typ, ok := act["type"].(string) - if !ok { - return nil, errors.WithDetailf(errBadActionType, "no action type provided on action %d", i) - } - decoder, ok := a.actionDecoder(typ) - if !ok { - return nil, errors.WithDetailf(errBadActionType, "unknown action type %q on action %d", typ, i) - } - - // Remarshal to JSON, the action may have been modified when we - // filtered aliases. - b, err := json.Marshal(act) - if err != nil { - return nil, err - } - action, err := decoder(b) - if err != nil { - return nil, errors.WithDetailf(errBadAction, "%s on action %d", err.Error(), i) - } - actions = append(actions, action) - } - - ttl := req.TTL.Duration - if ttl == 0 { - ttl = defaultTxTTL - } - maxTime := time.Now().Add(ttl) - - tpl, err := txbuilder.Build(ctx, req.Tx, actions, maxTime, req.TimeRange) - if errors.Root(err) == txbuilder.ErrAction { - // append each of the inner errors contained in the data. - var Errs string - for _, innerErr := range errors.Data(err)["actions"].([]error) { - Errs = Errs + "<" + innerErr.Error() + ">" - } - err = errors.New(err.Error() + "-" + Errs) - } - if err != nil { - return nil, err - } - - // ensure null is never returned for signing instructions - if tpl.SigningInstructions == nil { - tpl.SigningInstructions = []*txbuilder.SigningInstruction{} - } - return tpl, nil -} - -// POST /build-transaction -func (a *API) build(ctx context.Context, buildReqs *BuildRequest) Response { - subctx := reqid.NewSubContext(ctx, reqid.New()) - - tmpl, err := a.buildSingle(subctx, buildReqs) - if err != nil { - return NewErrorResponse(err) - } - - return NewSuccessResponse(tmpl) -} - -func (a *API) submitSingle(ctx context.Context, tpl *txbuilder.Template) (map[string]string, error) { - if tpl.Transaction == nil { - return nil, errors.Wrap(txbuilder.ErrMissingRawTx) - } - - if err := txbuilder.FinalizeTx(ctx, a.chain, tpl.Transaction); err != nil { - return nil, errors.Wrapf(err, "tx %s", tpl.Transaction.ID.String()) - } - - return map[string]string{"tx_id": tpl.Transaction.ID.String()}, nil -} - // finalizeTxWait calls FinalizeTx and then waits for confirmation of // the transaction. A nil error return means the transaction is // confirmed on the blockchain. ErrRejected means a conflicting tx is @@ -215,40 +76,3 @@ func (bcr *BlockchainReactor) waitForTxInBlock(ctx context.Context, tx *types.Tx } } } - -type submitTxResp struct { - TxID *bc.Hash `json:"tx_id"` -} - -// POST /submit-transaction -func (a *API) submit(ctx context.Context, ins struct { - Tx types.Tx `json:"raw_transaction"` -}) Response { - if err := txbuilder.FinalizeTx(ctx, a.chain, &ins.Tx); err != nil { - return NewErrorResponse(err) - } - - log.WithField("tx_id", ins.Tx.ID).Info("submit single tx") - return NewSuccessResponse(&submitTxResp{TxID: &ins.Tx.ID}) -} - -// POST /sign-submit-transaction -func (a *API) signSubmit(ctx context.Context, x struct { - Password []string `json:"password"` - Txs txbuilder.Template `json:"transaction"` -}) Response { - if err := txbuilder.Sign(ctx, &x.Txs, nil, x.Password[0], a.pseudohsmSignTemplate); err != nil { - log.WithField("build err", err).Error("fail on sign transaction.") - return NewErrorResponse(err) - } - log.Info("Sign Transaction complete.") - - txID, err := a.submitSingle(nil, &x.Txs) - if err != nil { - log.WithField("err", err).Error("submit single tx") - return NewErrorResponse(err) - } - - log.WithField("tx_id", txID["tx_id"]).Info("submit single tx") - return NewSuccessResponse(txID) -} diff --git a/blockchain/txfeeds.go b/blockchain/txfeeds.go index 3d8207b1..4e9ce30b 100644 --- a/blockchain/txfeeds.go +++ b/blockchain/txfeeds.go @@ -4,25 +4,10 @@ import ( "context" "encoding/json" - log "github.com/sirupsen/logrus" - - "github.com/bytom/blockchain/txfeed" "github.com/bytom/errors" ) -// POST /create-txfeed -func (a *API) createTxFeed(ctx context.Context, in struct { - Alias string `json:"alias"` - Filter string `json:"filter"` -}) Response { - if err := a.bcr.TxFeedTracker.Create(ctx, in.Alias, in.Filter); err != nil { - log.WithField("error", err).Error("Add TxFeed Failed") - return NewErrorResponse(err) - } - return NewSuccessResponse(nil) -} - -func (bcr *BlockchainReactor) getTxFeedByAlias(ctx context.Context, filter string) ([]byte, error) { +func (bcr *BlockchainReactor) GetTxFeedByAlias(ctx context.Context, filter string) ([]byte, error) { jf, err := json.Marshal(filter) if err != nil { return nil, err @@ -35,73 +20,3 @@ func (bcr *BlockchainReactor) getTxFeedByAlias(ctx context.Context, filter strin return value, nil } - -// POST /get-transaction-feed -func (a *API) getTxFeed(ctx context.Context, in struct { - Alias string `json:"alias,omitempty"` -}) Response { - var tmpTxFeed interface{} - rawTxfeed, err := a.bcr.getTxFeedByAlias(ctx, in.Alias) - if err != nil { - return NewErrorResponse(err) - } - err = json.Unmarshal(rawTxfeed, &tmpTxFeed) - if err != nil { - return NewErrorResponse(err) - } - data := map[string]interface{}{"txfeed": tmpTxFeed} - return NewSuccessResponse(data) -} - -// POST /delete-transaction-feed -func (a *API) deleteTxFeed(ctx context.Context, in struct { - Alias string `json:"alias,omitempty"` -}) Response { - if err := a.bcr.TxFeedTracker.Delete(ctx, in.Alias); err != nil { - return NewErrorResponse(err) - } - return NewSuccessResponse(nil) -} - -// POST /update-transaction-feed -func (a *API) updateTxFeed(ctx context.Context, in struct { - Alias string `json:"alias"` - Filter string `json:"filter"` -}) Response { - if err := a.bcr.TxFeedTracker.Delete(ctx, in.Alias); err != nil { - return NewErrorResponse(err) - } - if err := a.bcr.TxFeedTracker.Create(ctx, in.Alias, in.Filter); err != nil { - log.WithField("error", err).Error("Update TxFeed Failed") - return NewErrorResponse(err) - } - return NewSuccessResponse(nil) -} - -func (a *API) getTxFeeds() ([]txfeed.TxFeed, error) { - txFeed := txfeed.TxFeed{} - txFeeds := make([]txfeed.TxFeed, 0) - - iter := a.bcr.TxFeedTracker.DB.Iterator() - defer iter.Release() - - for iter.Next() { - if err := json.Unmarshal(iter.Value(), &txFeed); err != nil { - return nil, err - } - txFeeds = append(txFeeds, txFeed) - } - - return txFeeds, nil -} - -// listTxFeeds is an http handler for listing txfeeds. It does not take a filter. -// POST /list-transaction-feeds -func (a *API) listTxFeeds(ctx context.Context) Response { - txFeeds, err := a.getTxFeeds() - if err != nil { - return NewErrorResponse(err) - } - - return NewSuccessResponse(txFeeds) -} diff --git a/cmd/bytomcli/commands/key.go b/cmd/bytomcli/commands/key.go index b2c0d644..cb9a8f23 100644 --- a/cmd/bytomcli/commands/key.go +++ b/cmd/bytomcli/commands/key.go @@ -9,7 +9,7 @@ import ( "github.com/spf13/cobra" jww "github.com/spf13/jwalterweatherman" - "github.com/bytom/blockchain" + "github.com/bytom/api" "github.com/bytom/crypto/ed25519/chainkd" "github.com/bytom/util" ) @@ -128,7 +128,7 @@ var importPrivateCmd = &cobra.Command{ Short: "Import the private key", Args: cobra.ExactArgs(5), Run: func(cmd *cobra.Command, args []string) { - var params blockchain.KeyImportParams + var params api.KeyImportParams params.KeyAlias = args[0] params.XPrv = args[1] params.Password = args[3] diff --git a/cmd/bytomcli/commands/transaction.go b/cmd/bytomcli/commands/transaction.go index 2e284c47..edab4965 100644 --- a/cmd/bytomcli/commands/transaction.go +++ b/cmd/bytomcli/commands/transaction.go @@ -8,7 +8,7 @@ import ( "github.com/spf13/cobra" jww "github.com/spf13/jwalterweatherman" - "github.com/bytom/blockchain" + "github.com/bytom/api" "github.com/bytom/blockchain/txbuilder" "github.com/bytom/protocol/bc/types" "github.com/bytom/util" @@ -146,7 +146,7 @@ var buildTransactionCmd = &cobra.Command{ os.Exit(util.ErrLocalExe) } - var buildReq blockchain.BuildRequest + var buildReq api.BuildRequest if err := json.Unmarshal([]byte(buildReqStr), &buildReq); err != nil { jww.ERROR.Println(err) os.Exit(util.ErrLocalExe) diff --git a/cmd/miner/main.go b/cmd/miner/main.go index 2f65a106..ecccf3e9 100644 --- a/cmd/miner/main.go +++ b/cmd/miner/main.go @@ -5,7 +5,7 @@ import ( "fmt" "os" - "github.com/bytom/api" + "github.com/bytom/blockchain" "github.com/bytom/consensus/difficulty" "github.com/bytom/protocol/bc" "github.com/bytom/protocol/bc/types" @@ -61,7 +61,7 @@ func main() { fmt.Println(err) os.Exit(1) } - resp := &api.GetWorkResp{} + resp := &blockchain.GetWorkResp{} if err = json.Unmarshal(rawData, resp); err != nil { fmt.Println(err) os.Exit(1) diff --git a/node/node.go b/node/node.go index 502034e3..904fde9c 100755 --- a/node/node.go +++ b/node/node.go @@ -13,14 +13,15 @@ import ( cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" + "github.com/bytom/api" + "github.com/bytom/crypto/ed25519/chainkd" + bc "github.com/bytom/blockchain" "github.com/bytom/accesstoken" "github.com/bytom/account" "github.com/bytom/asset" - bc "github.com/bytom/blockchain" "github.com/bytom/blockchain/pseudohsm" "github.com/bytom/blockchain/txfeed" cfg "github.com/bytom/config" - "github.com/bytom/crypto/ed25519/chainkd" "github.com/bytom/database/leveldb" "github.com/bytom/env" "github.com/bytom/p2p" @@ -51,7 +52,8 @@ type Node struct { bcReactor *bc.BlockchainReactor wallet *w.Wallet accessTokens *accesstoken.CredentialStore - api *bc.API + api *api.API + chain *protocol.Chain } func NewNode(config *cfg.Config) *Node { @@ -162,6 +164,7 @@ func NewNode(config *cfg.Config) *Node { bcReactor: bcReactor, accessTokens: accessTokens, wallet: wallet, + chain: chain, } node.BaseService = *cmn.NewBaseService(nil, "Node", node) @@ -210,7 +213,7 @@ func lanchWebBroser(lanch bool) { } func (n *Node) initAndstartApiServer() { - n.api = bc.NewAPI(n.bcReactor, n.config) + n.api = api.NewAPI(n.bcReactor, n.wallet, n.chain, n.config) listenAddr := env.String("LISTEN", n.config.ApiAddress) n.api.StartServer(*listenAddr) diff --git a/util/util.go b/util/util.go index b15bcaed..5b072f93 100644 --- a/util/util.go +++ b/util/util.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/bytom/api" "github.com/bytom/blockchain" "github.com/bytom/blockchain/rpc" "github.com/bytom/env" @@ -76,7 +77,7 @@ func MustRPCClient() *rpc.Client { // Wrapper rpc call api. func ClientCall(path string, req ...interface{}) (interface{}, int) { - var response = &blockchain.Response{} + var response = &api.Response{} var request interface{} if req != nil { @@ -87,7 +88,7 @@ func ClientCall(path string, req ...interface{}) (interface{}, int) { client.Call(context.Background(), path, request, response) switch response.Status { - case blockchain.FAIL: + case api.FAIL: jww.ERROR.Println(response.Msg) return nil, ErrRemote case "": -- 2.11.0