"github.com/vapor/account"
"github.com/vapor/blockchain/txbuilder"
"github.com/vapor/errors"
- "github.com/vapor/net/http/reqid"
"github.com/vapor/protocol/bc"
"github.com/vapor/protocol/bc/types"
)
// 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)
+ tmpl, err := a.buildSingle(ctx, buildReqs)
if err != nil {
return NewErrorResponse(err)
}
// POST /build-chain-transactions
func (a *API) buildChainTxs(ctx context.Context, buildReqs *BuildRequest) Response {
- subctx := reqid.NewSubContext(ctx, reqid.New())
- tmpls, err := a.buildTxs(subctx, buildReqs)
+ tmpls, err := a.buildTxs(ctx, buildReqs)
if err != nil {
return NewErrorResponse(err)
}
"github.com/vapor/errors"
"github.com/vapor/net/http/httperror"
- "github.com/vapor/net/http/reqid"
)
// Bytom-specific header fields
}
// Propagate our request ID so that we can trace a request across nodes.
- req.Header.Add("Request-ID", reqid.FromContext(ctx))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", c.userAgent())
req.Header.Set(HeaderBlockchainID, c.BlockchainID)
consensusResult
)
-var (
- blockStoreKey = []byte{blockStore}
- blockHashesPrefix = []byte{blockHashes, colon}
- blockHeaderPrefix = []byte{blockHeader, colon}
- blockTransactonsPrefix = []byte{blockTransactons, colon}
- mainChainIndexPrefix = []byte{mainChainIndex, colon}
- txStatusPrefix = []byte{txStatus, colon}
- consensusResultPrefix = []byte{consensusResult, colon}
-)
-
func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
- bytes := db.Get(blockStoreKey)
+ bytes := db.Get([]byte{blockStore})
if bytes == nil {
return nil
}
func calcMainChainIndexPrefix(height uint64) []byte {
buf := [8]byte{}
binary.BigEndian.PutUint64(buf[:], height)
- return append(mainChainIndexPrefix, buf[:]...)
+ return append([]byte{mainChainIndex, colon}, buf[:]...)
}
func calcBlockHashesPrefix(height uint64) []byte {
buf := [8]byte{}
binary.BigEndian.PutUint64(buf[:], height)
- return append(blockHashesPrefix, buf[:]...)
+ return append([]byte{blockHashes, colon}, buf[:]...)
}
func calcBlockHeaderKey(hash *bc.Hash) []byte {
- return append(blockHeaderPrefix, hash.Bytes()...)
+ return append([]byte{blockHeader, colon}, hash.Bytes()...)
}
func calcBlockTransactionsKey(hash *bc.Hash) []byte {
- return append(blockTransactonsPrefix, hash.Bytes()...)
+ return append([]byte{blockTransactons, colon}, hash.Bytes()...)
}
func calcTxStatusKey(hash *bc.Hash) []byte {
- return append(txStatusPrefix, hash.Bytes()...)
+ return append([]byte{txStatus, colon}, hash.Bytes()...)
}
func calcConsensusResultKey(seq uint64) []byte {
buf := [8]byte{}
binary.BigEndian.PutUint64(buf[:], seq)
- return append(consensusResultPrefix, buf[:]...)
+ return append([]byte{consensusResult, colon}, buf[:]...)
}
// GetBlockHeader return the block header by given hash
if err != nil {
return err
}
- batch.Set(blockStoreKey, bytes)
+ batch.Set([]byte{blockStore}, bytes)
// save main chain blockHeaders
for _, bh := range mainBlockHeaders {
func saveUtxoView(batch dbm.Batch, view *state.UtxoViewpoint) error {
for key, entry := range view.Entries {
- if (entry.Type == storage.CrosschainUTXOType) && (!entry.Spent) {
+ if entry.Type == storage.CrosschainUTXOType && !entry.Spent {
batch.Delete(calcUtxoKey(&key))
continue
}
- if (entry.Type == storage.NormalUTXOType || entry.Type == storage.VoteUTXOType) && (entry.Spent) {
+ if entry.Type == storage.NormalUTXOType && entry.Spent {
batch.Delete(calcUtxoKey(&key))
continue
}
{
hash: bc.Hash{V0: 6},
utxoEntry: storage.NewUtxoEntry(storage.VoteUTXOType, 0, true),
- exist: false,
+ exist: true,
},
{
hash: bc.Hash{V0: 7},
+++ /dev/null
-// Package reqid creates request IDs and stores them in Contexts.
-package reqid
-
-import (
- "context"
- "crypto/rand"
- "encoding/hex"
- "net/http"
-
- log "github.com/sirupsen/logrus"
-)
-
-// key is an unexported type for keys defined in this package.
-// This prevents collisions with keys defined in other packages.
-type key int
-
-const (
- // reqIDKey is the key for request IDs in Contexts. It is
- // unexported; clients use NewContext and FromContext
- // instead of using this key directly.
- reqIDKey key = iota
- // subReqIDKey is the key for sub-request IDs in Contexts. It is
- // unexported; clients use NewSubContext and FromSubContext
- // instead of using this key directly.
- subReqIDKey
- // coreIDKey is the key for Chain-Core-ID request header field values.
- // It is only for statistics; don't use it for authorization.
- coreIDKey
- // pathKey is the key for the request path being handled.
- pathKey
- logModule = "reqid"
-)
-
-// New generates a random request ID.
-func New() string {
- // Given n IDs of length b bits, the probability that there will be a collision is bounded by
- // the number of pairs of IDs multiplied by the probability that any pair might collide:
- // p ≤ n(n - 1)/2 * 1/(2^b)
- //
- // We assume an upper bound of 1000 req/sec, which means that in a week there will be
- // n = 1000 * 604800 requests. If l = 10, b = 8*10, then p ≤ 1.512e-7, which is a suitably
- // low probability.
- l := 10
- b := make([]byte, l)
- _, err := rand.Read(b)
- if err != nil {
- log.WithFields(log.Fields{"module": logModule, "error": err}).Info("error making reqID")
- }
- return hex.EncodeToString(b)
-}
-
-// NewContext returns a new Context that carries reqid.
-// It also adds a log prefix to print the request ID using
-// package bytom/log.
-func NewContext(ctx context.Context, reqid string) context.Context {
- ctx = context.WithValue(ctx, reqIDKey, reqid)
- return ctx
-}
-
-// FromContext returns the request ID stored in ctx,
-// if any.
-func FromContext(ctx context.Context) string {
- reqID, _ := ctx.Value(reqIDKey).(string)
- return reqID
-}
-
-// CoreIDFromContext returns the Chain-Core-ID stored in ctx,
-// or the empty string.
-func CoreIDFromContext(ctx context.Context) string {
- id, _ := ctx.Value(coreIDKey).(string)
- return id
-}
-
-// PathFromContext returns the HTTP path stored in ctx,
-// or the empty string.
-func PathFromContext(ctx context.Context) string {
- path, _ := ctx.Value(pathKey).(string)
- return path
-}
-
-func NewSubContext(ctx context.Context, reqid string) context.Context {
- ctx = context.WithValue(ctx, subReqIDKey, reqid)
- return ctx
-}
-
-// FromSubContext returns the sub-request ID stored in ctx,
-// if any.
-func FromSubContext(ctx context.Context) string {
- subReqID, _ := ctx.Value(subReqIDKey).(string)
- return subReqID
-}
-
-func Handler(handler http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- })
-}
nextBlockTime += consensus.BlockTimeInterval
}
- isBlocker, err := b.chain.IsBlocker(&bestBlockHash, xpubStr, nextBlockTime)
+ blocker, err := b.chain.GetBlocker(&bestBlockHash, nextBlockTime)
if err != nil {
log.WithFields(log.Fields{"module": logModule, "error": err, "pubKey": xpubStr}).Error("fail on check is next blocker")
continue
}
- if !isBlocker {
+ if xpubStr != blocker {
continue
}
return fmt.Sprintf("%s:%s", blockHash, pubkey)
}
+func (c *Chain) checkDoubleSign(bh *types.BlockHeader, xPub string) error {
+ blockHashes, err := c.store.GetBlockHashesByHeight(bh.Height)
+ if err != nil {
+ return err
+ }
+
+ for _, blockHash := range blockHashes {
+ if *blockHash == bh.Hash() {
+ continue
+ }
+
+ blockHeader, err := c.store.GetBlockHeader(blockHash)
+ if err != nil {
+ return err
+ }
+
+ consensusNode, err := c.getConsensusNode(&blockHeader.PreviousBlockHash, xPub)
+ if err == errNotFoundConsensusNode {
+ continue
+ } else if err != nil {
+ return err
+ }
+
+ if blockHeader.BlockWitness.Get(consensusNode.Order) != nil {
+ return errDoubleSignBlock
+ }
+ }
+ return nil
+}
+
+func (c *Chain) checkNodeSign(bh *types.BlockHeader, consensusNode *state.ConsensusNode, signature []byte) error {
+ if !consensusNode.XPub.Verify(bh.Hash().Bytes(), signature) {
+ return errInvalidSignature
+ }
+
+ return c.checkDoubleSign(bh, consensusNode.XPub.String())
+}
+
func (c *Chain) isIrreversible(blockHeader *types.BlockHeader) bool {
consensusNodes, err := c.getConsensusNodes(&blockHeader.PreviousBlockHash)
if err != nil {
return signCount > len(consensusNodes)*2/3
}
-// GetConsensusResultByHash return vote result by block hash
-func (c *Chain) GetConsensusResultByHash(blockHash *bc.Hash) (*state.ConsensusResult, error) {
- blockHeader, err := c.store.GetBlockHeader(blockHash)
- if err != nil {
- return nil, err
- }
- return c.getConsensusResult(state.CalcVoteSeq(blockHeader.Height), blockHeader)
-}
-
-// IsBlocker returns whether the consensus node is a blocker at the specified time
-func (c *Chain) IsBlocker(prevBlockHash *bc.Hash, pubKey string, timeStamp uint64) (bool, error) {
- xPub, err := c.GetBlocker(prevBlockHash, timeStamp)
- if err != nil {
- return false, err
- }
- return xPub == pubKey, nil
-}
-
-// ProcessBlockSignature process the received block signature messages
-// return whether a block become irreversible, if so, the chain module must update status
-func (c *Chain) ProcessBlockSignature(signature, xPub []byte, blockHash *bc.Hash) error {
- xpubStr := hex.EncodeToString(xPub[:])
- blockHeader, _ := c.store.GetBlockHeader(blockHash)
-
- // save the signature if the block is not exist
- if blockHeader == nil {
- cacheKey := signCacheKey(blockHash.String(), xpubStr)
- c.signatureCache.Add(cacheKey, signature)
- return nil
- }
-
- consensusNode, err := c.getConsensusNode(&blockHeader.PreviousBlockHash, xpubStr)
- if err != nil {
+func (c *Chain) updateBlockSignature(blockHeader *types.BlockHeader, nodeOrder uint64, signature []byte) error {
+ blockHeader.Set(nodeOrder, signature)
+ if err := c.store.SaveBlockHeader(blockHeader); err != nil {
return err
}
- if blockHeader.BlockWitness.Get(consensusNode.Order) != nil {
+ if !c.isIrreversible(blockHeader) || blockHeader.Height <= c.lastIrrBlockHeader.Height {
return nil
}
- c.cond.L.Lock()
- defer c.cond.L.Unlock()
- if err := c.checkNodeSign(blockHeader, consensusNode, signature); err != nil {
- return err
- }
+ if c.InMainChain(blockHeader.Hash()) {
+ if err := c.store.SaveChainStatus(c.bestBlockHeader, blockHeader, []*types.BlockHeader{}, state.NewUtxoViewpoint(), []*state.ConsensusResult{}); err != nil {
+ return err
+ }
- if err := c.updateBlockSignature(blockHeader, consensusNode.Order, signature); err != nil {
- return err
+ c.lastIrrBlockHeader = blockHeader
+ } else {
+ // block is on a forked chain
+ log.WithFields(log.Fields{"module": logModule}).Info("majority votes received on forked chain")
+ tail, err := c.traceLongestChainTail(blockHeader)
+ if err != nil {
+ return err
+ }
+
+ return c.reorganizeChain(tail)
}
- return c.eventDispatcher.Post(event.BlockSignatureEvent{BlockHash: *blockHash, Signature: signature, XPub: xPub})
+ return nil
}
// validateSign verify the signatures of block, and return the number of correct signature
return err
}
+ blocker, err := c.GetBlocker(&block.PreviousBlockHash, block.Timestamp)
+ if err != nil {
+ return err
+ }
+
hasBlockerSign := false
blockHash := block.Hash()
for pubKey, node := range consensusNodeMap {
- if len(block.Witness) <= int(node.Order) {
- continue
- }
-
- if block.Get(node.Order) == nil {
+ if block.BlockWitness.Get(node.Order) == nil {
cachekey := signCacheKey(blockHash.String(), pubKey)
if signature, ok := c.signatureCache.Get(cachekey); ok {
block.Set(node.Order, signature.([]byte))
if err := c.checkNodeSign(&block.BlockHeader, node, block.Get(node.Order)); err == errDoubleSignBlock {
log.WithFields(log.Fields{"module": logModule, "blockHash": blockHash.String(), "pubKey": pubKey}).Warn("the consensus node double sign the same height of different block")
- block.Delete(node.Order)
+ block.BlockWitness.Delete(node.Order)
continue
} else if err != nil {
return err
}
- isBlocker, err := c.IsBlocker(&block.PreviousBlockHash, pubKey, block.Timestamp)
- if err != nil {
- return err
- }
-
- if isBlocker {
+ if blocker == pubKey {
hasBlockerSign = true
}
-
}
if !hasBlockerSign {
return nil
}
-func (c *Chain) checkNodeSign(bh *types.BlockHeader, consensusNode *state.ConsensusNode, signature []byte) error {
- if !consensusNode.XPub.Verify(bh.Hash().Bytes(), signature) {
- return errInvalidSignature
+// ProcessBlockSignature process the received block signature messages
+// return whether a block become irreversible, if so, the chain module must update status
+func (c *Chain) ProcessBlockSignature(signature, xPub []byte, blockHash *bc.Hash) error {
+ xpubStr := hex.EncodeToString(xPub[:])
+ blockHeader, _ := c.store.GetBlockHeader(blockHash)
+
+ // save the signature if the block is not exist
+ if blockHeader == nil {
+ cacheKey := signCacheKey(blockHash.String(), xpubStr)
+ c.signatureCache.Add(cacheKey, signature)
+ return nil
}
- blockHashes, err := c.store.GetBlockHashesByHeight(bh.Height)
+ consensusNode, err := c.getConsensusNode(&blockHeader.PreviousBlockHash, xpubStr)
if err != nil {
return err
}
- for _, blockHash := range blockHashes {
- if *blockHash == bh.Hash() {
- continue
- }
-
- blockHeader, err := c.store.GetBlockHeader(blockHash)
- if err != nil {
- return err
- }
-
- consensusNode, err := c.getConsensusNode(&blockHeader.PreviousBlockHash, consensusNode.XPub.String())
- if err != nil && err != errNotFoundConsensusNode {
- return err
- }
+ if blockHeader.BlockWitness.Get(consensusNode.Order) != nil {
+ return nil
+ }
- if err == errNotFoundConsensusNode {
- continue
- }
+ c.cond.L.Lock()
+ defer c.cond.L.Unlock()
+ if err := c.checkNodeSign(blockHeader, consensusNode, signature); err != nil {
+ return err
+ }
- if blockHeader.BlockWitness.Get(consensusNode.Order) != nil {
- return errDoubleSignBlock
- }
+ if err := c.updateBlockSignature(blockHeader, consensusNode.Order, signature); err != nil {
+ return err
}
- return nil
+ return c.eventDispatcher.Post(event.BlockSignatureEvent{BlockHash: *blockHash, Signature: signature, XPub: xPub})
}
// SignBlock signing the block if current node is consensus node
return nil, err
}
- //check double sign in same block height
- blockHashes, err := c.store.GetBlockHashesByHeight(block.Height)
- if err != nil {
+ if err := c.checkDoubleSign(&block.BlockHeader, node.XPub.String()); err == errDoubleSignBlock {
+ return nil, nil
+ } else if err != nil {
return nil, err
}
- for _, hash := range blockHashes {
- blockHeader, err := c.store.GetBlockHeader(hash)
- if err != nil {
- return nil, err
- }
-
- // Has already signed the same height block
- if blockHeader.BlockWitness.Get(node.Order) != nil {
- return nil, nil
- }
- }
-
signature := block.Get(node.Order)
if len(signature) == 0 {
signature = xprv.Sign(block.Hash().Bytes())
}
return signature, nil
}
-
-func (c *Chain) updateBlockSignature(blockHeader *types.BlockHeader, nodeOrder uint64, signature []byte) error {
- blockHeader.Set(nodeOrder, signature)
- if err := c.store.SaveBlockHeader(blockHeader); err != nil {
- return err
- }
-
- if !c.isIrreversible(blockHeader) || blockHeader.Height <= c.lastIrrBlockHeader.Height {
- return nil
- }
-
- if c.InMainChain(blockHeader.Hash()) {
- if err := c.store.SaveChainStatus(c.bestBlockHeader, blockHeader, []*types.BlockHeader{}, state.NewUtxoViewpoint(), []*state.ConsensusResult{}); err != nil {
- return err
- }
-
- c.lastIrrBlockHeader = blockHeader
- } else {
- // block is on a forked chain
- log.WithFields(log.Fields{"module": logModule}).Info("majority votes received on forked chain")
- tail, err := c.traceLongestChainTail(blockHeader)
- if err != nil {
- return err
- }
-
- return c.reorganizeChain(tail)
- }
- return nil
-}
errNotFoundBlockNode = errors.New("can not find block node")
)
-func (c *Chain) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) {
- consensusNodeMap, err := c.getConsensusNodes(prevBlockHash)
- if err != nil {
- return nil, err
- }
-
- node, exist := consensusNodeMap[pubkey]
- if !exist {
- return nil, errNotFoundConsensusNode
- }
- return node, nil
-}
-
-// GetBlocker return blocker by specified timestamp
-func (c *Chain) GetBlocker(prevBlockHash *bc.Hash, timeStamp uint64) (string, error) {
- consensusNodeMap, err := c.getConsensusNodes(prevBlockHash)
- if err != nil {
- return "", err
- }
-
- prevVoteRoundLastBlock, err := c.getPrevRoundLastBlock(prevBlockHash)
- if err != nil {
- return "", err
- }
-
- startTimestamp := prevVoteRoundLastBlock.Timestamp + consensus.BlockTimeInterval
- order := getBlockerOrder(startTimestamp, timeStamp, uint64(len(consensusNodeMap)))
- for xPub, consensusNode := range consensusNodeMap {
- if consensusNode.Order == order {
- return xPub, nil
- }
- }
-
- // impossible occur
- return "", errors.New("can not find blocker by given timestamp")
+func (c *Chain) getBestConsensusResult() (*state.ConsensusResult, error) {
+ bestBlockHeader := c.bestBlockHeader
+ seq := state.CalcVoteSeq(bestBlockHeader.Height)
+ return c.getConsensusResult(seq, bestBlockHeader)
}
func getBlockerOrder(startTimestamp, blockTimestamp, numOfConsensusNode uint64) uint64 {
return (blockTimestamp - lastRoundStartTime) / (consensus.BlockNumEachNode * consensus.BlockTimeInterval)
}
-func (c *Chain) getPrevRoundLastBlock(prevBlockHash *bc.Hash) (*types.BlockHeader, error) {
- blockHeader, err := c.store.GetBlockHeader(prevBlockHash)
+func (c *Chain) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) {
+ consensusNodeMap, err := c.getConsensusNodes(prevBlockHash)
if err != nil {
- return nil, errNotFoundBlockNode
+ return nil, err
}
- for blockHeader.Height%consensus.RoundVoteBlockNums != 0 {
- blockHeader, err = c.store.GetBlockHeader(&blockHeader.PreviousBlockHash)
- if err != nil {
- return nil, err
- }
+ node, exist := consensusNodeMap[pubkey]
+ if !exist {
+ return nil, errNotFoundConsensusNode
}
- return blockHeader, nil
+ return node, nil
}
func (c *Chain) getConsensusNodes(prevBlockHash *bc.Hash) (map[string]*state.ConsensusNode, error) {
return consensusResult.ConsensusNodes()
}
-func (c *Chain) getBestConsensusResult() (*state.ConsensusResult, error) {
- bestBlockHeader := c.bestBlockHeader
- seq := state.CalcVoteSeq(bestBlockHeader.Height)
- return c.getConsensusResult(seq, bestBlockHeader)
-}
-
// getConsensusResult return the vote result
// seq represent the sequence of vote
// blockHeader represent the chain in which the result of the vote is located
return consensusResult, nil
}
+func (c *Chain) getPrevRoundLastBlock(prevBlockHash *bc.Hash) (*types.BlockHeader, error) {
+ blockHeader, err := c.store.GetBlockHeader(prevBlockHash)
+ if err != nil {
+ return nil, errNotFoundBlockNode
+ }
+
+ for blockHeader.Height%consensus.RoundVoteBlockNums != 0 {
+ blockHeader, err = c.store.GetBlockHeader(&blockHeader.PreviousBlockHash)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return blockHeader, nil
+}
+
func (c *Chain) reorganizeConsensusResult(consensusResult *state.ConsensusResult, blockHeader *types.BlockHeader) error {
mainChainBlockHeader, err := c.store.GetBlockHeader(&consensusResult.BlockHash)
if err != nil {
}
return nil
}
+
+// GetBlocker return blocker by specified timestamp
+func (c *Chain) GetBlocker(prevBlockHash *bc.Hash, timeStamp uint64) (string, error) {
+ consensusNodeMap, err := c.getConsensusNodes(prevBlockHash)
+ if err != nil {
+ return "", err
+ }
+
+ prevVoteRoundLastBlock, err := c.getPrevRoundLastBlock(prevBlockHash)
+ if err != nil {
+ return "", err
+ }
+
+ startTimestamp := prevVoteRoundLastBlock.Timestamp + consensus.BlockTimeInterval
+ order := getBlockerOrder(startTimestamp, timeStamp, uint64(len(consensusNodeMap)))
+ for xPub, consensusNode := range consensusNodeMap {
+ if consensusNode.Order == order {
+ return xPub, nil
+ }
+ }
+
+ // impossible occur
+ return "", errors.New("can not find blocker by given timestamp")
+}
+
+// GetConsensusResultByHash return vote result by block hash
+func (c *Chain) GetConsensusResultByHash(blockHash *bc.Hash) (*state.ConsensusResult, error) {
+ blockHeader, err := c.store.GetBlockHeader(blockHash)
+ if err != nil {
+ return nil, err
+ }
+ return c.getConsensusResult(state.CalcVoteSeq(blockHeader.Height), blockHeader)
+}
func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.BlockHeader, error) {
longestTail, workQueue := blockHeader, []*types.BlockHeader{blockHeader}
- for len(workQueue) > 0 {
+ for ; len(workQueue) > 0; workQueue = workQueue[1:] {
currentHeader := workQueue[0]
currentHash := currentHeader.Hash()
- workQueue = workQueue[1:]
hashes, err := c.store.GetBlockHashesByHeight(currentHeader.Height + 1)
if err != nil {
return nil, err
},
},
err: true,
- gasValid: true,
+ gasValid: false,
},
{
category: "double spend",
},
},
err: true,
- gasValid: true,
+ gasValid: false,
},
}
const (
validateWorkerNum = 32
- ruleAA = 142500
)
// validate transaction error
return errors.New("incorrect asset_id while checking CrossChainInput")
}
- code := config.FederationWScript(config.CommonConfig)
prog := &bc.Program{
VmVersion: e.ControlProgram.VmVersion,
- Code: code,
+ Code: config.FederationWScript(config.CommonConfig),
}
if _, err := vm.Verify(NewTxVMContext(vs, e, prog, e.WitnessArguments), consensus.DefaultGasCredit); err != nil {
return errors.Wrap(err, "checking cross-chain input control program")
}
+ eq, err := mainchainOutput.Source.Value.Equal(e.WitnessDestination.Value)
+ if err != nil {
+ return err
+ }
+
+ if !eq {
+ return errors.WithDetailf(
+ ErrMismatchedValue,
+ "previous output is for %d unit(s) of %x, spend wants %d unit(s) of %x",
+ mainchainOutput.Source.Value.Amount,
+ mainchainOutput.Source.Value.AssetId.Bytes(),
+ e.WitnessDestination.Value.Amount,
+ e.WitnessDestination.Value.AssetId.Bytes(),
+ )
+ }
+
vs2 := *vs
vs2.destPos = 0
if err = checkValidDest(&vs2, e.WitnessDestination); err != nil {
if err != nil {
return errors.Wrap(err, "getting vetoInput prevout")
}
+
if len(voteOutput.Vote) != 64 {
return ErrVotePubKey
}
func checkInputID(tx *bc.Tx, blockHeight uint64) error {
for _, id := range tx.InputIDs {
- if blockHeight >= ruleAA && id.IsZero() {
+ if id.IsZero() {
return ErrEmptyInputIDs
}
}
block *bc.Block
}
-type validateTxResult struct {
+// ValidateTxResult is the result of async tx validate
+type ValidateTxResult struct {
i int
gasStatus *GasState
err error
}
-func (r *validateTxResult) GetGasState() *GasState {
+// GetGasState return the gasStatus
+func (r *ValidateTxResult) GetGasState() *GasState {
return r.gasStatus
}
-func (r *validateTxResult) GetError() error {
+// GetError return the err
+func (r *ValidateTxResult) GetError() error {
return r.err
}
-func validateTxWorker(workCh chan *validateTxWork, resultCh chan *validateTxResult, closeCh chan struct{}, wg *sync.WaitGroup) {
+func validateTxWorker(workCh chan *validateTxWork, resultCh chan *ValidateTxResult, closeCh chan struct{}, wg *sync.WaitGroup) {
for {
select {
case work := <-workCh:
gasStatus, err := ValidateTx(work.tx, work.block)
- resultCh <- &validateTxResult{i: work.i, gasStatus: gasStatus, err: err}
+ resultCh <- &ValidateTxResult{i: work.i, gasStatus: gasStatus, err: err}
case <-closeCh:
wg.Done()
return
}
}
-func ValidateTxs(txs []*bc.Tx, block *bc.Block) []*validateTxResult {
+// ValidateTxs validates txs in async mode
+func ValidateTxs(txs []*bc.Tx, block *bc.Block) []*ValidateTxResult {
txSize := len(txs)
//init the goroutine validate worker
var wg sync.WaitGroup
workCh := make(chan *validateTxWork, txSize)
- resultCh := make(chan *validateTxResult, txSize)
+ resultCh := make(chan *ValidateTxResult, txSize)
closeCh := make(chan struct{})
for i := 0; i <= validateWorkerNum && i < txSize; i++ {
wg.Add(1)
}
//collect validate results
- results := make([]*validateTxResult, txSize)
+ results := make([]*ValidateTxResult, txSize)
for i := 0; i < txSize; i++ {
result := <-resultCh
results[result.i] = result
txInputs := make([]*types.TxInput, 0, len(inputs))
txOutputs := make([]*types.TxOutput, 0, len(outputs))
- for _, amount := range inputs {
- txInput := types.NewSpendInput(nil, *sourceID, *consensus.BTMAssetID, amount, 0, ctrlProgram)
+ for i, amount := range inputs {
+ txInput := types.NewSpendInput(nil, *sourceID, *consensus.BTMAssetID, amount, uint64(i), ctrlProgram)
txInputs = append(txInputs, txInput)
}
types.MapTx(&types.TxData{
SerializedSize: 1,
Inputs: []*types.TxInput{
- types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
types.NewCoinbaseInput(nil),
+ types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
},
Outputs: []*types.TxOutput{
types.NewIntraChainOutput(*consensus.BTMAssetID, 888, cp),