OSDN Git Service

feat(bcrp): convert call bcrp contract program (#1891)
authorDeKaiju <longjinglv@gmail.com>
Thu, 15 Apr 2021 07:44:11 +0000 (15:44 +0800)
committerGitHub <noreply@github.com>
Thu, 15 Apr 2021 07:44:11 +0000 (15:44 +0800)
* feat(bcrp): convert call bcrp contract program

* refactor(bcrp): code refactoring

* refactor(bcrp): code refactoring

* refactor(bcrp): code refactoring

* refactor(bcrp): code refactoring

21 files changed:
consensus/bcrp/bcrp.go [new file with mode: 0644]
consensus/bcrp/bcrp_test.go [new file with mode: 0644]
consensus/general.go
consensus/segwit/segwit.go
consensus/segwit/segwit_test.go
database/contract_view.go
database/store.go
protocol/block.go
protocol/state/contract_view.go
protocol/store.go
protocol/tx.go
protocol/txpool_test.go
protocol/validation/block.go
protocol/validation/block_test.go
protocol/validation/test/tx_ugly_test.go
protocol/validation/tx.go
protocol/validation/tx_scene_test.go
protocol/validation/tx_test.go
protocol/validation/vmcontext.go
test/block_test_util.go
test/integration/standard_transaction_test.go

diff --git a/consensus/bcrp/bcrp.go b/consensus/bcrp/bcrp.go
new file mode 100644 (file)
index 0000000..515424c
--- /dev/null
@@ -0,0 +1,89 @@
+package bcrp
+
+import (
+       "bytes"
+
+       "github.com/bytom/bytom/consensus"
+       "github.com/bytom/bytom/errors"
+       "github.com/bytom/bytom/protocol/vm"
+)
+
+//bcrp bytom contract register protocol
+const bcrp = "bcrp"
+
+// IsBCRPScript checks if a control program is bytom contract register protocol
+// BCRP script format: OP_FAIL + OP_PUSHDATA1 + "04" + hex("bcrp") + OP_PUSHDATA1 + "01" + "01" + OP_PUSHDATA1 + len(contract) + contract
+func IsBCRPScript(prog []byte) bool {
+       inst, err := vm.ParseProgram(prog)
+       if err != nil {
+               return false
+       }
+
+       if len(inst) != 4 {
+               return false
+       }
+
+       if inst[0].Op != vm.OP_FAIL {
+               return false
+       }
+
+       if inst[1].Op != vm.OP_PUSHDATA1 {
+               return false
+       }
+
+       if !bytes.Equal(inst[1].Data, []byte(bcrp)) {
+               return false
+       }
+
+       if inst[2].Op != vm.OP_PUSHDATA1 {
+               return false
+       }
+
+       // version 1
+       if !bytes.Equal(inst[2].Data, []byte{byte(1)}) {
+               return false
+       }
+
+       if inst[3].Op != vm.OP_PUSHDATA1 {
+               return false
+       }
+
+       return true
+}
+
+// IsCallBCRPScript checks if a control program is call contract registered by bytom contract register protocol
+// call BCRP script format: OP_1 + OP_DATA_32 + SHA3-256(contract)
+func IsCallBCRPScript(prog []byte) bool {
+       insts, err := vm.ParseProgram(prog)
+       if err != nil {
+               return false
+       }
+
+       if len(insts) != 2 {
+               return false
+       }
+
+       if insts[0].Op != vm.OP_1 {
+               return false
+       }
+
+       return insts[1].Op == vm.OP_DATA_32 && len(insts[1].Data) == consensus.BCRPContractHashDataSize
+}
+
+// ParseContractHash parse contract hash from call BCRP script
+// call BCRP script format: OP_1 + OP_DATA_32 + SHA3-256(contract)
+func ParseContractHash(prog []byte) ([32]byte, error) {
+       insts, err := vm.ParseProgram(prog)
+       if err != nil {
+               return [32]byte{}, err
+       }
+
+       if len(insts) != 2 {
+               return [32]byte{}, errors.New("unsupport program")
+       }
+
+       var hash [32]byte
+       copy(hash[:], insts[1].Data)
+
+       return hash, nil
+}
diff --git a/consensus/bcrp/bcrp_test.go b/consensus/bcrp/bcrp_test.go
new file mode 100644 (file)
index 0000000..85d9427
--- /dev/null
@@ -0,0 +1,91 @@
+package bcrp
+
+import (
+       "encoding/hex"
+       "testing"
+)
+
+func TestIsBCRPScript(t *testing.T) {
+       tests := []struct {
+               program  string
+               expected bool
+       }{
+               {
+                       program:  "",
+                       expected: false,
+               },
+               {
+                       program:  "ae20ac20f5cdb9ada2ae9836bcfff32126d6b885aa3f73ee111a95d1bf37f3904aca5151ad",
+                       expected: false,
+               },
+               {
+                       program:  "694c04626372704c01014c2820e9108d3ca8049800727f6a3505b3a2710dc579405dde03c250f16d9a7e1e6e787403ae7cac00c0",
+                       expected: false,
+               },
+               {
+                       program:  "6a4c04424352504c01014c2820e9108d3ca8049800727f6a3505b3a2710dc579405dde03c250f16d9a7e1e6e787403ae7cac00c0",
+                       expected: false,
+               },
+               {
+                       program:  "6a4c04626372704c01024c2820e9108d3ca8049800727f6a3505b3a2710dc579405dde03c250f16d9a7e1e6e787403ae7cac00c0",
+                       expected: false,
+               },
+               {
+                       program:  "6a4c04626372704c01014c2820e9108d3ca8049800727f6a3505b3a2710dc579405dde03c250f16d9a7e1e6e787403ae7cac00c0",
+                       expected: true,
+               },
+       }
+
+       for i, test := range tests {
+               program, err := hex.DecodeString(test.program)
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               expected := IsBCRPScript(program)
+               if expected != test.expected {
+                       t.Errorf("TestIsTemplateRegister #%d failed: got %v want %v", i, expected, test.expected)
+               }
+       }
+}
+
+func TestIsCallBCRPScript(t *testing.T) {
+       tests := []struct {
+               program  string
+               expected bool
+       }{
+               {
+                       program:  "",
+                       expected: false,
+               },
+               {
+                       program:  "6a4c04626372704c01014c2820e9108d3ca8049800727f6a3505b3a2710dc579405dde03c250f16d9a7e1e6e787403ae7cac00c0",
+                       expected: false,
+               },
+               {
+                       program:  "00204e4f02d43bf50171f7f25d046b7f016002da410fc00d2e8902e7b170c98cf946",
+                       expected: false,
+               },
+               {
+                       program:  "514c204e4f02d43bf50171f7f25d046b7f016002da410fc00d2e8902e7b170c98cf946",
+                       expected: false,
+               },
+               {
+                       program:  "51204e4f02d43bf50171f7f25d046b7f016002da410fc00d2e8902e7b170c98cf946",
+                       expected: true,
+               },
+       }
+
+       for i, test := range tests {
+               program, err := hex.DecodeString(test.program)
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               expected := IsCallBCRPScript(program)
+               if expected != test.expected {
+                       t.Errorf("TestIsCallBCRPScript #%d failed: got %v want %v", i, expected, test.expected)
+               }
+       }
+}
+
index 5aa83c6..bc4bc6f 100644 (file)
@@ -28,6 +28,7 @@ const (
 
        PayToWitnessPubKeyHashDataSize = 20
        PayToWitnessScriptHashDataSize = 32
+       BCRPContractHashDataSize       = 32
        CoinbaseArbitrarySizeLimit     = 128
 
        BTMAlias = "BTM"
index 6f70d2c..b85c4e8 100644 (file)
@@ -1,7 +1,6 @@
 package segwit
 
 import (
-       "bytes"
        "errors"
 
        "github.com/bytom/bytom/consensus"
@@ -9,9 +8,6 @@ import (
        "github.com/bytom/bytom/protocol/vm/vmutil"
 )
 
-//bcrp bytom contract register protocol
-const bcrp = "bcrp"
-
 func IsP2WScript(prog []byte) bool {
        return IsP2WPKHScript(prog) || IsP2WSHScript(prog) || IsStraightforward(prog)
 }
@@ -55,46 +51,6 @@ func IsP2WSHScript(prog []byte) bool {
        return insts[1].Op == vm.OP_DATA_32 && len(insts[1].Data) == consensus.PayToWitnessScriptHashDataSize
 }
 
-// IsBCRPScript checks if a control program is bytom contract register protocol
-// BCRP script format: OP_FAIL + OP_PUSHDATA1 + "04" + hex("bcrp") + OP_PUSHDATA1 + "01" + "01" + OP_PUSHDATA1 + len(contract) + contract
-func IsBCRPScript(prog []byte) bool {
-       inst, err := vm.ParseProgram(prog)
-       if err != nil {
-               return false
-       }
-
-       if len(inst) != 4 {
-               return false
-       }
-
-       if inst[0].Op != vm.OP_FAIL {
-               return false
-       }
-
-       if inst[1].Op != vm.OP_PUSHDATA1 {
-               return false
-       }
-
-       if !bytes.Equal(inst[1].Data, []byte(bcrp)) {
-               return false
-       }
-
-       if inst[2].Op != vm.OP_PUSHDATA1 {
-               return false
-       }
-
-       // version 1
-       if !bytes.Equal(inst[2].Data, []byte{byte(1)}) {
-               return false
-       }
-
-       if inst[3].Op != vm.OP_PUSHDATA1 {
-               return false
-       }
-
-       return true
-}
-
 func ConvertP2PKHSigProgram(prog []byte) ([]byte, error) {
        insts, err := vm.ParseProgram(prog)
        if err != nil {
index dfc7cd8..26cc62d 100644 (file)
@@ -118,48 +118,4 @@ func TestProgramType(t *testing.T) {
                        t.Errorf("case #%d (%s) got %t, expect %t", i, c.desc, c.fun(progBytes), c.yes)
                }
        }
-}
-
-func TestIsBCRPScript(t *testing.T) {
-       tests := []struct {
-               program  string
-               expected bool
-       }{
-               {
-                       program:  "",
-                       expected: false,
-               },
-               {
-                       program:  "ae20ac20f5cdb9ada2ae9836bcfff32126d6b885aa3f73ee111a95d1bf37f3904aca5151ad",
-                       expected: false,
-               },
-               {
-                       program:  "694c04626372704c01014c2820e9108d3ca8049800727f6a3505b3a2710dc579405dde03c250f16d9a7e1e6e787403ae7cac00c0",
-                       expected: false,
-               },
-               {
-                       program:  "6a4c04424352504c01014c2820e9108d3ca8049800727f6a3505b3a2710dc579405dde03c250f16d9a7e1e6e787403ae7cac00c0",
-                       expected: false,
-               },
-               {
-                       program:  "6a4c04626372704c01024c2820e9108d3ca8049800727f6a3505b3a2710dc579405dde03c250f16d9a7e1e6e787403ae7cac00c0",
-                       expected: false,
-               },
-               {
-                       program:  "6a4c04626372704c01014c2820e9108d3ca8049800727f6a3505b3a2710dc579405dde03c250f16d9a7e1e6e787403ae7cac00c0",
-                       expected: true,
-               },
-       }
-
-       for i, test := range tests {
-               program, err := hex.DecodeString(test.program)
-               if err != nil {
-                       t.Fatal(err)
-               }
-
-               expected := IsBCRPScript(program)
-               if expected != test.expected {
-                       t.Errorf("TestIsTemplateRegister #%d failed: got %v want %v", i, expected, test.expected)
-               }
-       }
-}
+}
\ No newline at end of file
index 9e8c54a..58c4294 100644 (file)
@@ -4,6 +4,7 @@ import (
        "bytes"
 
        dbm "github.com/bytom/bytom/database/leveldb"
+       "github.com/bytom/bytom/errors"
        "github.com/bytom/bytom/protocol/state"
 )
 
@@ -40,3 +41,17 @@ func deleteContractView(db dbm.DB, batch dbm.Batch, view *state.ContractViewpoin
        }
        return nil
 }
+
+func getContract(db dbm.DB, hash [32]byte) ([]byte, error) {
+       data := db.Get(CalcContractKey(hash))
+       if data == nil {
+               return nil, errors.New("can't find the registered contract by contract hash")
+       }
+
+       // data:"txID+program.Code" len(txID) == 32
+       if len(data) <= 32 {
+               return nil, errors.New("registered contract format error")
+       }
+
+       return data[32:], nil
+}
index b2b7e18..cf9da66 100644 (file)
@@ -90,6 +90,10 @@ func (s *Store) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) {
        return getUtxo(s.db, hash)
 }
 
+func (s *Store) GetContract(hash [32]byte) ([]byte, error) {
+       return getContract(s.db, hash)
+}
+
 // BlockExist check if the block is stored in disk
 func (s *Store) BlockExist(hash *bc.Hash) bool {
        block, err := s.cache.lookup(hash)
index 9caf812..e0c03d1 100644 (file)
@@ -189,7 +189,7 @@ func (c *Chain) saveBlock(block *types.Block) error {
        bcBlock := types.MapBlock(block)
        parent := c.index.GetNode(&block.PreviousBlockHash)
 
-       if err := validation.ValidateBlock(bcBlock, parent); err != nil {
+       if err := validation.ValidateBlock(bcBlock, parent, c.ProgramConverter); err != nil {
                return errors.Sub(ErrBadBlock, err)
        }
 
index c756a08..cb7111d 100644 (file)
@@ -1,7 +1,7 @@
 package state
 
 import (
-       "github.com/bytom/bytom/consensus/segwit"
+       "github.com/bytom/bytom/consensus/bcrp"
        "github.com/bytom/bytom/crypto/sha3pool"
        "github.com/bytom/bytom/protocol/bc/types"
 )
@@ -25,7 +25,7 @@ func (view *ContractViewpoint) ApplyBlock(block *types.Block) error {
        for _, tx := range block.Transactions {
                for _, output := range tx.Outputs {
                        program := output.ControlProgram
-                       if !segwit.IsBCRPScript(program) {
+                       if !bcrp.IsBCRPScript(program) {
                                continue
                        }
                        var hash [32]byte
@@ -41,7 +41,7 @@ func (view *ContractViewpoint) DetachBlock(block *types.Block) error {
        for _, tx := range block.Transactions {
                for _, output := range tx.Outputs {
                        program := output.ControlProgram
-                       if !segwit.IsBCRPScript(program) {
+                       if !bcrp.IsBCRPScript(program) {
                                continue
                        }
                        var hash [32]byte
index 4001721..31ebe36 100644 (file)
@@ -16,6 +16,7 @@ type Store interface {
        GetStoreStatus() *BlockStoreState
        GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error
        GetUtxo(*bc.Hash) (*storage.UtxoEntry, error)
+       GetContract(hash [32]byte) ([]byte, error)
 
        GetCheckpoint(*bc.Hash) (*state.Checkpoint, error)
        GetCheckpointsByHeight(uint64) ([]*state.Checkpoint, error)
index 29c1908..d1f7a9d 100644 (file)
@@ -3,6 +3,7 @@ package protocol
 import (
        log "github.com/sirupsen/logrus"
 
+       "github.com/bytom/bytom/consensus/bcrp"
        "github.com/bytom/bytom/errors"
        "github.com/bytom/bytom/protocol/bc"
        "github.com/bytom/bytom/protocol/bc/types"
@@ -32,7 +33,7 @@ func (c *Chain) ValidateTx(tx *types.Tx) (bool, error) {
        }
 
        bh := c.BestBlockHeader()
-       gasStatus, err := validation.ValidateTx(tx.Tx, types.MapBlock(&types.Block{BlockHeader: *bh}))
+       gasStatus, err := validation.ValidateTx(tx.Tx, types.MapBlock(&types.Block{BlockHeader: *bh}), c.ProgramConverter)
        if err != nil {
                log.WithFields(log.Fields{"module": logModule, "tx_id": tx.Tx.ID.String(), "error": err}).Info("transaction status fail")
                c.txPool.AddErrCache(&tx.ID, err)
@@ -41,3 +42,13 @@ func (c *Chain) ValidateTx(tx *types.Tx) (bool, error) {
 
        return c.txPool.ProcessTransaction(tx, bh.Height, gasStatus.BTMValue)
 }
+
+//ProgramConverter convert program. Only for BCRP now
+func (c *Chain) ProgramConverter(prog []byte) ([]byte, error) {
+       hash, err := bcrp.ParseContractHash(prog)
+       if err != nil {
+               return nil, err
+       }
+
+       return c.store.GetContract(hash)
+}
index 1dd0d95..08e2a9d 100644 (file)
@@ -106,6 +106,7 @@ func (s *mockStore) GetBlock(*bc.Hash) (*types.Block, error)
 func (s *mockStore) GetStoreStatus() *BlockStoreState                             { return nil }
 func (s *mockStore) GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error     { return nil }
 func (s *mockStore) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error)                 { return nil, nil }
+func (s *mockStore) GetContract(hash [32]byte) ([]byte, error)                    { return nil, nil }
 func (s *mockStore) LoadBlockIndex(uint64) (*state.BlockIndex, error)             { return nil, nil }
 func (s *mockStore) SaveBlock(*types.Block) error                                 { return nil }
 func (s *mockStore) SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint, *state.ContractViewpoint) error {
@@ -605,6 +606,7 @@ func (s *mockStore1) GetTransactionsUtxo(utxoView *state.UtxoViewpoint, tx []*bc
        return nil
 }
 func (s *mockStore1) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error)        { return nil, nil }
+func (s *mockStore1) GetContract(hash [32]byte) ([]byte, error)           { return nil, nil }
 func (s *mockStore1) LoadBlockIndex(uint64) (*state.BlockIndex, error)    { return nil, nil }
 func (s *mockStore1) SaveBlock(*types.Block) error { return nil }
 func (s *mockStore1) SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint, *state.ContractViewpoint) error { return nil}
index 0bc5523..027edba 100644 (file)
@@ -76,7 +76,7 @@ func ValidateBlockHeader(b *bc.Block, parent *state.BlockNode) error {
 }
 
 // ValidateBlock validates a block and the transactions within.
-func ValidateBlock(b *bc.Block, parent *state.BlockNode) error {
+func ValidateBlock(b *bc.Block, parent *state.BlockNode, converter ProgramConverterFunc) error {
        startTime := time.Now()
        if err := ValidateBlockHeader(b, parent); err != nil {
                return err
@@ -84,7 +84,7 @@ func ValidateBlock(b *bc.Block, parent *state.BlockNode) error {
 
        blockGasSum := uint64(0)
        coinbaseAmount := consensus.BlockSubsidy(b.BlockHeader.Height)
-       validateResults := ValidateTxs(b.Transactions, b)
+       validateResults := ValidateTxs(b.Transactions, b, converter)
        for i, validateResult := range validateResults {
                if validateResult.err != nil {
                        return errors.Wrapf(validateResult.err, "validate of transaction %d of %d, gas_valid:%v", i, len(b.Transactions), validateResult.gasStatus.GasValid)
index 13b52e1..ee4a70d 100644 (file)
@@ -216,6 +216,7 @@ func TestValidateBlockHeader(t *testing.T) {
 // TestValidateBlock test the ValidateBlock function
 func TestValidateBlock(t *testing.T) {
        cp, _ := vmutil.DefaultCoinbaseProgram()
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
        cases := []struct {
                desc   string
                block  *bc.Block
@@ -314,7 +315,7 @@ func TestValidateBlock(t *testing.T) {
        }
 
        for i, c := range cases {
-               err := ValidateBlock(c.block, c.parent)
+               err := ValidateBlock(c.block, c.parent, converter)
                if rootErr(err) != c.err {
                        t.Errorf("case #%d (%s) got error %s, want %s", i, c.desc, err, c.err)
                }
@@ -323,8 +324,8 @@ func TestValidateBlock(t *testing.T) {
 
 // TestGasOverBlockLimit check if the gas of the block has the max limit (blocktest#1012)
 func TestGasOverBlockLimit(t *testing.T) {
-
        cp, _ := vmutil.DefaultCoinbaseProgram()
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
        parent := &state.BlockNode{
                Version:   1,
                Height:    0,
@@ -363,7 +364,7 @@ func TestGasOverBlockLimit(t *testing.T) {
                }))
        }
 
-       if err := ValidateBlock(block, parent); err != errOverBlockLimit {
+       if err := ValidateBlock(block, parent, converter); err != errOverBlockLimit {
                t.Errorf("got error %s, want %s", err, errOverBlockLimit)
        }
 }
index 27265a2..3382685 100644 (file)
@@ -834,11 +834,12 @@ func TestValidateUglyTx(t *testing.T) {
                tx := types.NewTx(c.txData)
                mockSignTx(tx, c.insts)
                bcTx := types.MapTx(&c.txData)
+               converter := func(prog []byte) ([]byte, error) { return nil, nil }
 
                _, err := validation.ValidateTx(bcTx, &bc.Block{
                        BlockHeader:  &bc.BlockHeader{Height: 1},
                        Transactions: []*bc.Tx{bcTx},
-               })
+               }, converter)
                if !c.err && err != nil {
                        t.Errorf("case #%d (%s) expect no error, got error %s", i, c.desc, err)
                }
index 7f5f7dd..05a4e6c 100644 (file)
@@ -103,16 +103,20 @@ func (g *GasState) updateUsage(gasLeft int64) error {
        return nil
 }
 
+// ProgramConverterFunc represent a func convert control program
+type ProgramConverterFunc func(prog []byte) ([]byte, error)
+
 // validationState contains the context that must propagate through
 // the transaction graph when validating entries.
 type validationState struct {
        block     *bc.Block
        tx        *bc.Tx
        gasStatus *GasState
-       entryID   bc.Hash           // The ID of the nearest enclosing entry
-       sourcePos uint64            // The source position, for validate ValueSources
-       destPos   uint64            // The destination position, for validate ValueDestinations
-       cache     map[bc.Hash]error // Memoized per-entry validation results
+       entryID   bc.Hash              // The ID of the nearest enclosing entry
+       sourcePos uint64               // The source position, for validate ValueSources
+       destPos   uint64               // The destination position, for validate ValueDestinations
+       cache     map[bc.Hash]error    // Memoized per-entry validation results
+       converter ProgramConverterFunc // Program converter function
 }
 
 func checkValid(vs *validationState, e bc.Entry) (err error) {
@@ -493,7 +497,7 @@ func checkTimeRange(tx *bc.Tx, block *bc.Block) error {
 }
 
 // ValidateTx validates a transaction.
-func ValidateTx(tx *bc.Tx, block *bc.Block) (*GasState, error) {
+func ValidateTx(tx *bc.Tx, block *bc.Block, converter ProgramConverterFunc) (*GasState, error) {
        if block.Version == 1 && tx.Version != 1 {
                return &GasState{GasValid: false}, errors.WithDetailf(ErrTxVersion, "block version %d, transaction version %d", block.Version, tx.Version)
        }
@@ -516,6 +520,7 @@ func ValidateTx(tx *bc.Tx, block *bc.Block) (*GasState, error) {
                entryID:   tx.ID,
                gasStatus: &GasState{GasValid: false},
                cache:     make(map[bc.Hash]error),
+               converter: converter,
        }
 
        if err := checkValid(vs, tx.TxHeader); err != nil {
@@ -552,16 +557,16 @@ func (r *ValidateTxResult) GetError() error {
        return r.err
 }
 
-func validateTxWorker(workCh chan *validateTxWork, resultCh chan *ValidateTxResult, wg *sync.WaitGroup) {
+func validateTxWorker(workCh chan *validateTxWork, resultCh chan *ValidateTxResult, wg *sync.WaitGroup, converter ProgramConverterFunc) {
        for work := range workCh {
-               gasStatus, err := ValidateTx(work.tx, work.block)
+               gasStatus, err := ValidateTx(work.tx, work.block, converter)
                resultCh <- &ValidateTxResult{i: work.i, gasStatus: gasStatus, err: err}
        }
        wg.Done()
 }
 
 // ValidateTxs validates txs in async mode
-func ValidateTxs(txs []*bc.Tx, block *bc.Block) []*ValidateTxResult {
+func ValidateTxs(txs []*bc.Tx, block *bc.Block, converter ProgramConverterFunc) []*ValidateTxResult {
        txSize := len(txs)
        validateWorkerNum := runtime.NumCPU()
        //init the goroutine validate worker
@@ -570,7 +575,7 @@ func ValidateTxs(txs []*bc.Tx, block *bc.Block) []*ValidateTxResult {
        resultCh := make(chan *ValidateTxResult, txSize)
        for i := 0; i <= validateWorkerNum && i < txSize; i++ {
                wg.Add(1)
-               go validateTxWorker(workCh, resultCh, &wg)
+               go validateTxWorker(workCh, resultCh, &wg, converter)
        }
 
        //sent the works
index b294fb7..b3286b2 100644 (file)
@@ -11,6 +11,7 @@ import (
 )
 
 func TestValidateTx(t *testing.T) {
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
        cases := []struct {
                desc     string
                txData   *types.TxData
@@ -264,7 +265,7 @@ func TestValidateTx(t *testing.T) {
        }
 
        for i, c := range cases {
-               gasStatus, err := ValidateTx(types.MapTx(c.txData), mockBlock())
+               gasStatus, err := ValidateTx(types.MapTx(c.txData), mockBlock(), converter)
                if rootErr(err) != c.err {
                        t.Errorf("case #%d (%s) got error %s, want %s; validationState is:\n", i, c.desc, err, c.err)
                }
index 624c9ca..542e75e 100644 (file)
@@ -227,6 +227,7 @@ func TestGasStatus(t *testing.T) {
 func TestOverflow(t *testing.T) {
        sourceID := &bc.Hash{V0: 9999}
        ctrlProgram := []byte{byte(vm.OP_TRUE)}
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
        newTx := func(inputs []uint64, outputs []uint64) *bc.Tx {
                txInputs := make([]*types.TxInput, 0, len(inputs))
                txOutputs := make([]*types.TxOutput, 0, len(outputs))
@@ -305,7 +306,7 @@ func TestOverflow(t *testing.T) {
 
        for i, c := range cases {
                tx := newTx(c.inputs, c.outputs)
-               if _, err := ValidateTx(tx, mockBlock()); rootErr(err) != c.err {
+               if _, err := ValidateTx(tx, mockBlock(), converter); rootErr(err) != c.err {
                        t.Fatalf("case %d test failed, want %s, have %s", i, c.err, rootErr(err))
                }
        }
@@ -695,6 +696,7 @@ func TestTxValidation(t *testing.T) {
 func TestCoinbase(t *testing.T) {
        cp, _ := vmutil.DefaultCoinbaseProgram()
        retire, _ := vmutil.RetireProgram([]byte{})
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
        CbTx := types.MapTx(&types.TxData{
                SerializedSize: 1,
                Inputs: []*types.TxInput{
@@ -829,7 +831,7 @@ func TestCoinbase(t *testing.T) {
        }
 
        for i, c := range cases {
-               gasStatus, err := ValidateTx(c.block.Transactions[c.txIndex], c.block)
+               gasStatus, err := ValidateTx(c.block.Transactions[c.txIndex], c.block, converter)
 
                if rootErr(err) != c.err {
                        t.Errorf("#%d got error %s, want %s", i, err, c.err)
@@ -842,6 +844,7 @@ func TestCoinbase(t *testing.T) {
 
 func TestRuleAA(t *testing.T) {
        testData := "070100040161015f9bc47dda88eee18c7433340c16e054cabee4318a8d638e873be19e979df81dc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e3f9f5c80e01011600147c7662d92bd5e77454736f94731c60a6e9cbc69f6302404a17a5995b8163ee448719b462a5694b22a35522dd9883333fd462cc3d0aabf049445c5cbb911a40e1906a5bea99b23b1a79e215eeb1a818d8b1dd27e06f3004200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940160015ee334d4fe18398f0232d2aca7050388ce4ee5ae82c8148d7f0cea748438b65135ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ace6842001011600147c7662d92bd5e77454736f94731c60a6e9cbc69f6302404a17a5995b8163ee448719b462a5694b22a35522dd9883333fd462cc3d0aabf049445c5cbb911a40e1906a5bea99b23b1a79e215eeb1a818d8b1dd27e06f3004200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940161015f9bc47dda88eee18c7433340c16e054cabee4318a8d638e873be19e979df81dc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e3f9f5c80e01011600147c7662d92bd5e77454736f94731c60a6e9cbc69f63024062c29b20941e7f762c3afae232f61d8dac1c544825931e391408c6715c408ef69f494a1b3b61ce380ddee0c8b18ecac2b46ef96a62eebb6ec40f9f545410870a200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940160015ee334d4fe18398f0232d2aca7050388ce4ee5ae82c8148d7f0cea748438b65135ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ace6842001011600147c7662d92bd5e77454736f94731c60a6e9cbc69f630240e443d66c75b4d5fa71676d60b0b067e6941f06349f31e5f73a7d51a73f5797632b2e01e8584cd1c8730dc16df075866b0c796bd7870182e2da4b37188208fe02200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940201003effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08ba3fae80e01160014aac0345165045e612b3d7363f39a372bead80ce70001003effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08fe0fae80e01160014aac0345165045e612b3d7363f39a372bead80ce700"
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
        tx := types.Tx{}
        if err := tx.UnmarshalText([]byte(testData)); err != nil {
                t.Errorf("fail on unmarshal txData: %s", err)
@@ -871,7 +874,7 @@ func TestRuleAA(t *testing.T) {
        }
 
        for i, c := range cases {
-               _, err := ValidateTx(tx.Tx, c.block)
+               _, err := ValidateTx(tx.Tx, c.block, converter)
                if rootErr(err) != c.err {
                        t.Errorf("#%d got error %s, want %s", i, err, c.err)
                }
@@ -880,6 +883,7 @@ func TestRuleAA(t *testing.T) {
 
 // TestTimeRange test the checkTimeRange function (txtest#1004)
 func TestTimeRange(t *testing.T) {
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
        cases := []struct {
                timeRange uint64
                err       bool
@@ -922,7 +926,7 @@ func TestTimeRange(t *testing.T) {
 
        for i, c := range cases {
                tx.TimeRange = c.timeRange
-               if _, err := ValidateTx(tx, block); (err != nil) != c.err {
+               if _, err := ValidateTx(tx, block, converter); (err != nil) != c.err {
                        t.Errorf("#%d got error %t, want %t", i, !c.err, c.err)
                }
        }
@@ -976,6 +980,7 @@ func TestStandardTx(t *testing.T) {
 }
 
 func TestValidateTxVersion(t *testing.T) {
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
        cases := []struct {
                desc  string
                block *bc.Block
@@ -1014,7 +1019,7 @@ func TestValidateTxVersion(t *testing.T) {
        }
 
        for i, c := range cases {
-               if _, err := ValidateTx(c.block.Transactions[0], c.block); rootErr(err) != c.err {
+               if _, err := ValidateTx(c.block.Transactions[0], c.block, converter); rootErr(err) != c.err {
                        t.Errorf("case #%d (%s) got error %t, want %t", i, c.desc, err, c.err)
                }
        }
index cd7a2ad..0dffada 100644 (file)
@@ -3,6 +3,7 @@ package validation
 import (
        "bytes"
 
+       "github.com/bytom/bytom/consensus/bcrp"
        "github.com/bytom/bytom/consensus/segwit"
        "github.com/bytom/bytom/crypto/sha3pool"
        "github.com/bytom/bytom/errors"
@@ -65,7 +66,7 @@ func NewTxVMContext(vs *validationState, entry bc.Entry, prog *bc.Program, args
 
        result := &vm.Context{
                VMVersion: prog.VmVersion,
-               Code:      witnessProgram(prog.Code),
+               Code:      convertProgram(prog.Code, vs.converter),
                Arguments: args,
 
                EntryID: entryID.Bytes(),
@@ -85,7 +86,7 @@ func NewTxVMContext(vs *validationState, entry bc.Entry, prog *bc.Program, args
        return result
 }
 
-func witnessProgram(prog []byte) []byte {
+func convertProgram(prog []byte, converter ProgramConverterFunc) []byte {
        if segwit.IsP2WPKHScript(prog) {
                if witnessProg, err := segwit.ConvertP2PKHSigProgram([]byte(prog)); err == nil {
                        return witnessProg
@@ -94,6 +95,10 @@ func witnessProgram(prog []byte) []byte {
                if witnessProg, err := segwit.ConvertP2SHProgram([]byte(prog)); err == nil {
                        return witnessProg
                }
+       } else if bcrp.IsCallBCRPScript(prog) {
+               if contractProg, err := converter(prog); err == nil {
+                       return contractProg
+               }
        }
        return prog
 }
index 0014f3c..9083bc5 100644 (file)
@@ -29,7 +29,7 @@ func NewBlock(chain *protocol.Chain, txs []*types.Tx, controlProgram []byte) (*t
 
        bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: preBlockHeader.Height + 1}}
        for _, tx := range txs {
-               gasStatus, err := validation.ValidateTx(tx.Tx, bcBlock)
+               gasStatus, err := validation.ValidateTx(tx.Tx, bcBlock, chain.ProgramConverter)
                if err != nil {
                        continue
                }
index d4faa03..f3f56d8 100644 (file)
@@ -10,10 +10,10 @@ import (
        "github.com/bytom/bytom/blockchain/pseudohsm"
        "github.com/bytom/bytom/blockchain/signers"
        "github.com/bytom/bytom/crypto/ed25519/chainkd"
+       dbm "github.com/bytom/bytom/database/leveldb"
        "github.com/bytom/bytom/protocol/bc/types"
        "github.com/bytom/bytom/protocol/validation"
        "github.com/bytom/bytom/test"
-       dbm "github.com/bytom/bytom/database/leveldb"
 )
 
 func TestP2PKH(t *testing.T) {
@@ -62,7 +62,8 @@ func TestP2PKH(t *testing.T) {
        }
 
        tx.SerializedSize = 1
-       if _, err = validation.ValidateTx(types.MapTx(tx), test.MockBlock()); err != nil {
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
+       if _, err = validation.ValidateTx(types.MapTx(tx), test.MockBlock(), converter); err != nil {
                t.Fatal(err)
        }
 }
@@ -126,7 +127,8 @@ func TestBip0032P2PKH(t *testing.T) {
        }
 
        tx.SerializedSize = 1
-       if _, err = validation.ValidateTx(types.MapTx(tx), test.MockBlock()); err != nil {
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
+       if _, err = validation.ValidateTx(types.MapTx(tx), test.MockBlock(), converter); err != nil {
                t.Fatal(err)
        }
 }
@@ -187,7 +189,8 @@ func TestP2SH(t *testing.T) {
        }
 
        tx.SerializedSize = 1
-       if _, err = validation.ValidateTx(types.MapTx(tx), test.MockBlock()); err != nil {
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
+       if _, err = validation.ValidateTx(types.MapTx(tx), test.MockBlock(), converter); err != nil {
                t.Fatal(err)
        }
 }
@@ -260,7 +263,8 @@ func TestBip0032P2SH(t *testing.T) {
        }
 
        tx.SerializedSize = 1
-       if _, err = validation.ValidateTx(types.MapTx(tx), test.MockBlock()); err != nil {
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
+       if _, err = validation.ValidateTx(types.MapTx(tx), test.MockBlock(), converter); err != nil {
                t.Fatal(err)
        }
 }
@@ -331,7 +335,8 @@ func TestMutilNodeSign(t *testing.T) {
        }
 
        tx.SerializedSize = 1
-       if _, err = validation.ValidateTx(types.MapTx(tx), test.MockBlock()); err != nil {
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
+       if _, err = validation.ValidateTx(types.MapTx(tx), test.MockBlock(), converter); err != nil {
                t.Fatal(err)
        }
 }
@@ -415,7 +420,8 @@ func TestBip0032MutilNodeSign(t *testing.T) {
        }
 
        tx.SerializedSize = 1
-       if _, err = validation.ValidateTx(types.MapTx(tx), test.MockBlock()); err != nil {
+       converter := func(prog []byte) ([]byte, error) { return nil, nil }
+       if _, err = validation.ValidateTx(types.MapTx(tx), test.MockBlock(), converter); err != nil {
                t.Fatal(err)
        }
 }