OSDN Git Service

vm state data (#1930)
authorboomyl <563807243@qq.com>
Mon, 17 May 2021 07:51:02 +0000 (15:51 +0800)
committerGitHub <noreply@github.com>
Mon, 17 May 2021 07:51:02 +0000 (15:51 +0800)
* vm state data

* fix comments:2d bytes compare

* fix altstack copying data

* fix comments

* fix comments

protocol/validation/tx.go
protocol/validation/vmcontext.go
protocol/validation/vmcontext_test.go
protocol/vm/context.go
protocol/vm/introspection.go
protocol/vm/introspection_test.go
protocol/vm/vm.go

index fda9007..aa0414c 100644 (file)
@@ -234,7 +234,7 @@ func checkValid(vs *validationState, e bc.Entry) (err error) {
                        return errors.WithDetailf(ErrMismatchedAssetID, "asset ID is %x, issuance wants %x", computedAssetID.Bytes(), e.Value.AssetId.Bytes())
                }
 
-               gasLeft, err := vm.Verify(NewTxVMContext(vs, e, e.WitnessAssetDefinition.IssuanceProgram, e.WitnessArguments), vs.gasStatus.GasLeft)
+               gasLeft, err := vm.Verify(NewTxVMContext(vs, e, e.WitnessAssetDefinition.IssuanceProgram, &bc.StateData{}, e.WitnessArguments), vs.gasStatus.GasLeft)
                if err != nil {
                        return errors.Wrap(err, "checking issuance program")
                }
@@ -257,7 +257,7 @@ func checkValid(vs *validationState, e bc.Entry) (err error) {
                        return errors.Wrap(err, "getting spend prevout")
                }
 
-               gasLeft, err := vm.Verify(NewTxVMContext(vs, e, spentOutput.ControlProgram, e.WitnessArguments), vs.gasStatus.GasLeft)
+               gasLeft, err := vm.Verify(NewTxVMContext(vs, e, spentOutput.ControlProgram, spentOutput.StateData, e.WitnessArguments), vs.gasStatus.GasLeft)
                if err != nil {
                        return errors.Wrap(err, "checking control program")
                }
index 4dd1b8a..e2fefe8 100644 (file)
@@ -12,7 +12,7 @@ import (
 )
 
 // NewTxVMContext generates the vm.Context for BVM
-func NewTxVMContext(vs *validationState, entry bc.Entry, prog *bc.Program, args [][]byte) *vm.Context {
+func NewTxVMContext(vs *validationState, entry bc.Entry, prog *bc.Program, stateData *bc.StateData, args [][]byte) *vm.Context {
        var (
                tx          = vs.tx
                blockHeight = vs.block.BlockHeader.GetHeight()
@@ -67,6 +67,7 @@ func NewTxVMContext(vs *validationState, entry bc.Entry, prog *bc.Program, args
        result := &vm.Context{
                VMVersion: prog.VmVersion,
                Code:      convertProgram(prog.Code, vs.converter),
+               StateData: stateData.StateData,
                Arguments: args,
 
                EntryID: entryID.Bytes(),
@@ -108,18 +109,19 @@ type entryContext struct {
        entries map[bc.Hash]bc.Entry
 }
 
-func (ec *entryContext) checkOutput(index uint64, amount uint64, assetID []byte, vmVersion uint64, code []byte, expansion bool) (bool, error) {
+func (ec *entryContext) checkOutput(index uint64, amount uint64, assetID []byte, vmVersion uint64, code []byte, state [][]byte, expansion bool) (bool, error) {
        checkEntry := func(e bc.Entry) (bool, error) {
-               check := func(prog *bc.Program, value *bc.AssetAmount) bool {
+               check := func(prog *bc.Program, value *bc.AssetAmount, stateData *bc.StateData) bool {
                        return (prog.VmVersion == vmVersion &&
                                bytes.Equal(prog.Code, code) &&
                                bytes.Equal(value.AssetId.Bytes(), assetID) &&
-                               value.Amount == amount)
+                               value.Amount == amount &&
+                               bytesEqual(stateData.StateData, state))
                }
 
                switch e := e.(type) {
                case *bc.Output:
-                       return check(e.ControlProgram, e.Source.Value), nil
+                       return check(e.ControlProgram, e.Source.Value, e.StateData), nil
 
                case *bc.Retirement:
                        var prog bc.Program
@@ -131,7 +133,7 @@ func (ec *entryContext) checkOutput(index uint64, amount uint64, assetID []byte,
                                // (The spec always requires prog.VmVersion to be zero.)
                                prog.Code = code
                        }
-                       return check(&prog, e.Source.Value), nil
+                       return check(&prog, e.Source.Value, &bc.StateData{}), nil
                }
 
                return false, vm.ErrContext
@@ -182,3 +184,21 @@ func (ec *entryContext) checkOutput(index uint64, amount uint64, assetID []byte,
 
        return false, vm.ErrContext
 }
+
+func bytesEqual(a, b [][]byte) bool {
+       if (a == nil) != (b == nil) {
+               return false
+       }
+
+       if len(a) != len(b) {
+               return false
+       }
+
+       for i, v := range a {
+               if !bytes.Equal(v, b[i]) {
+                       return false
+               }
+       }
+
+       return true
+}
index 59e88a6..c6c1fff 100644 (file)
@@ -17,11 +17,11 @@ func TestCheckOutput(t *testing.T) {
                        types.NewIssuanceInput(nil, 6, []byte("issueprog"), nil, nil),
                },
                Outputs: []*types.TxOutput{
-                       types.NewOriginalTxOutput(bc.NewAssetID([32]byte{3}), 8, []byte("wrongprog"), nil),
-                       types.NewOriginalTxOutput(bc.NewAssetID([32]byte{3}), 8, []byte("controlprog"), nil),
-                       types.NewOriginalTxOutput(bc.NewAssetID([32]byte{2}), 8, []byte("controlprog"), nil),
-                       types.NewOriginalTxOutput(bc.NewAssetID([32]byte{2}), 7, []byte("controlprog"), nil),
-                       types.NewOriginalTxOutput(bc.NewAssetID([32]byte{2}), 7, []byte("controlprog"), nil),
+                       types.NewOriginalTxOutput(bc.NewAssetID([32]byte{3}), 8, []byte("wrongprog"), [][]byte{[]byte("statedata")}),
+                       types.NewOriginalTxOutput(bc.NewAssetID([32]byte{3}), 8, []byte("controlprog"), [][]byte{[]byte("wrongstatedata")}),
+                       types.NewOriginalTxOutput(bc.NewAssetID([32]byte{2}), 8, []byte("controlprog"), [][]byte{[]byte("statedata")}),
+                       types.NewOriginalTxOutput(bc.NewAssetID([32]byte{2}), 7, []byte("controlprog"), [][]byte{[]byte("statedata")}),
+                       types.NewOriginalTxOutput(bc.NewAssetID([32]byte{2}), 7, []byte("controlprog"), [][]byte{[]byte("statedata")}),
                },
        })
 
@@ -37,6 +37,7 @@ func TestCheckOutput(t *testing.T) {
                assetID   []byte
                vmVersion uint64
                code      []byte
+               state     [][]byte
 
                wantErr error
                wantOk  bool
@@ -47,6 +48,7 @@ func TestCheckOutput(t *testing.T) {
                        assetID:   append([]byte{2}, make([]byte, 31)...),
                        vmVersion: 1,
                        code:      []byte("controlprog"),
+                       state:     [][]byte{[]byte("statedata")},
                        wantOk:    true,
                },
                {
@@ -55,6 +57,7 @@ func TestCheckOutput(t *testing.T) {
                        assetID:   append([]byte{2}, make([]byte, 31)...),
                        vmVersion: 1,
                        code:      []byte("controlprog"),
+                       state:     [][]byte{[]byte("statedata")},
                        wantOk:    true,
                },
                {
@@ -62,7 +65,16 @@ func TestCheckOutput(t *testing.T) {
                        amount:    1,
                        assetID:   append([]byte{9}, make([]byte, 31)...),
                        vmVersion: 1,
-                       code:      []byte("missingprog"),
+                       code:      []byte("controlprog"),
+                       wantOk:    false,
+               },
+               {
+                       index:     1,
+                       amount:    8,
+                       assetID:   append([]byte{3}, make([]byte, 31)...),
+                       vmVersion: 1,
+                       code:      []byte("controlprog"),
+                       state:     [][]byte{[]byte("missingstatedata")},
                        wantOk:    false,
                },
                {
@@ -77,13 +89,13 @@ func TestCheckOutput(t *testing.T) {
 
        for i, test := range cases {
                t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
-                       gotOk, err := txCtx.checkOutput(test.index, test.amount, test.assetID, test.vmVersion, test.code, false)
+                       gotOk, err := txCtx.checkOutput(test.index, test.amount, test.assetID, test.vmVersion, test.code, test.state, false)
                        if g := errors.Root(err); g != test.wantErr {
-                               t.Errorf("checkOutput(%v, %v, %x, %v, %x) err = %v, want %v", test.index, test.amount, test.assetID, test.vmVersion, test.code, g, test.wantErr)
+                               t.Errorf("checkOutput(%v, %v, %x, %v, %x, %v) err = %v, want %v", test.index, test.amount, test.assetID, test.vmVersion, test.code, test.state, g, test.wantErr)
                                return
                        }
                        if gotOk != test.wantOk {
-                               t.Errorf("checkOutput(%v, %v, %x, %v, %x) ok = %t, want %v", test.index, test.amount, test.assetID, test.vmVersion, test.code, gotOk, test.wantOk)
+                               t.Errorf("checkOutput(%v, %v, %x, %v, %x, %v) ok = %t, want %v", test.index, test.amount, test.assetID, test.vmVersion, test.code, test.state, gotOk, test.wantOk)
                        }
 
                })
index 853e91c..7134a40 100644 (file)
@@ -12,6 +12,7 @@ package vm
 type Context struct {
        VMVersion uint64
        Code      []byte
+       StateData [][]byte
        Arguments [][]byte
 
        EntryID []byte
@@ -31,5 +32,5 @@ type Context struct {
        SpentOutputID *[]byte
 
        TxSigHash   func() []byte
-       CheckOutput func(index uint64, amount uint64, assetID []byte, vmVersion uint64, code []byte, expansion bool) (bool, error)
+       CheckOutput func(index uint64, amount uint64, assetID []byte, vmVersion uint64, code []byte, state [][]byte, expansion bool) (bool, error)
 }
index 36b71c7..1809578 100644 (file)
@@ -47,7 +47,7 @@ func opCheckOutput(vm *virtualMachine) error {
                return ErrContext
        }
 
-       ok, err := vm.context.CheckOutput(uint64(index), amount, assetID, uint64(vmVersion), code, vm.expansionReserved)
+       ok, err := vm.context.CheckOutput(uint64(index), amount, assetID, uint64(vmVersion), code, vm.altStack, vm.expansionReserved)
        if err != nil {
                return err
        }
index 5ed369a..2189908 100644 (file)
@@ -106,8 +106,9 @@ func TestIntrospectionOps(t *testing.T) {
                                {1},
                                []byte("missingprog"),
                        },
+                       altStack: [][]byte{[]byte("statedata")},
                        context: &Context{
-                               CheckOutput: func(uint64, uint64, []byte, uint64, []byte, bool) (bool, error) {
+                               CheckOutput: func(uint64, uint64, []byte, uint64, []byte, [][]byte, bool) (bool, error) {
                                        return false, nil
                                },
                        },
@@ -116,6 +117,7 @@ func TestIntrospectionOps(t *testing.T) {
                        runLimit:     50062,
                        deferredCost: -78,
                        dataStack:    [][]byte{{}},
+                       altStack:     [][]byte{[]byte("statedata")},
                },
        }, {
                op: OP_CHECKOUTPUT,
@@ -128,7 +130,8 @@ func TestIntrospectionOps(t *testing.T) {
                                Int64Bytes(-1),
                                []byte("controlprog"),
                        },
-                       context: &Context{},
+                       altStack: [][]byte{[]byte("statedata")},
+                       context:  &Context{},
                },
                wantErr: ErrBadValue,
        }, {
@@ -142,7 +145,8 @@ func TestIntrospectionOps(t *testing.T) {
                                {1},
                                []byte("controlprog"),
                        },
-                       context: &Context{},
+                       altStack: [][]byte{[]byte("statedata")},
+                       context:  &Context{},
                },
                wantErr: ErrBadValue,
        }, {
@@ -156,7 +160,8 @@ func TestIntrospectionOps(t *testing.T) {
                                {1},
                                []byte("controlprog"),
                        },
-                       context: &Context{},
+                       altStack: [][]byte{[]byte("statedata")},
+                       context:  &Context{},
                },
                wantErr: ErrBadValue,
        }, {
@@ -170,8 +175,9 @@ func TestIntrospectionOps(t *testing.T) {
                                {1},
                                []byte("controlprog"),
                        },
+                       altStack: [][]byte{[]byte("statedata")},
                        context: &Context{
-                               CheckOutput: func(uint64, uint64, []byte, uint64, []byte, bool) (bool, error) {
+                               CheckOutput: func(uint64, uint64, []byte, uint64, []byte, [][]byte, bool) (bool, error) {
                                        return false, ErrBadValue
                                },
                        },
@@ -189,7 +195,8 @@ func TestIntrospectionOps(t *testing.T) {
                                {1},
                                []byte("controlprog"),
                        },
-                       context: &Context{},
+                       altStack: [][]byte{[]byte("statedata")},
+                       context:  &Context{},
                },
                wantErr: ErrRunLimitExceeded,
        }, {
index 839d17d..898e0f0 100644 (file)
@@ -59,6 +59,12 @@ func Verify(context *Context, gasLimit int64) (gasLeft int64, err error) {
                runLimit:          gasLimit,
                context:           context,
        }
+       stateData := context.StateData
+       for i, state := range stateData {
+               if err = vm.pushAlt(state, false); err != nil {
+                       return vm.runLimit, errors.Wrapf(err, "pushing initial statedata %d", i)
+               }
+       }
 
        args := context.Arguments
        for i, arg := range args {
@@ -152,6 +158,21 @@ func (vm *virtualMachine) push(data []byte, deferred bool) error {
        return nil
 }
 
+func (vm *virtualMachine) pushAlt(data []byte, deferred bool) error {
+       cost := 8 + int64(len(data))
+       if deferred {
+               vm.deferCost(cost)
+       } else {
+               err := vm.applyCost(cost)
+               if err != nil {
+                       return err
+               }
+       }
+       vm.altStack = append(vm.altStack, data)
+
+       return nil
+}
+
 func (vm *virtualMachine) pushBool(b bool, deferred bool) error {
        return vm.push(BoolBytes(b), deferred)
 }