Skip to content

Commit

Permalink
Feature fix writeset errors (#129)
Browse files Browse the repository at this point in the history
* fix integ test

* fix mnemonic path

* don't use write set if there is error, also, print stack trace
  • Loading branch information
borispovod authored May 26, 2020
1 parent 0f55988 commit ecde272
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 1 deletion.
122 changes: 122 additions & 0 deletions x/vm/internal/keeper/keeper_integ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,59 @@ script {
}
`

const errorScript = `
script {
use 0x0::Account;
use 0x0::DFI;
use 0x0::Transaction;
use 0x0::Coins;
use 0x0::Event;
fun main(c: u64) {
let a = Account::withdraw_from_sender<DFI::T>(523);
let b = Account::withdraw_from_sender<Coins::BTC>(1);
let event_handle = Event::new_event_handle<u64>();
Event::emit_event(&mut event_handle, 10);
Event::destroy_handle(event_handle);
Transaction::assert(c == 1000, 122);
Account::deposit_to_sender(a);
Account::deposit_to_sender(b);
}
}
`

// print events.
func printEvent(event sdk.Event, t *testing.T) {
t.Logf("Event: %s\n", event.Type)
for _, attr := range event.Attributes {
t.Logf("%s = %s\n", attr.Key, attr.Value)
}
}

// check that sub status exists.
func checkSubStatus(events sdk.Events, subStatus uint64, t *testing.T) {
found := false

for _, event := range events {
if event.Type == types.EventTypeContractStatus {
// find error
for _, attr := range event.Attributes {
if string(attr.Key) == types.AttrKeySubStatus {
require.EqualValues(t, attr.Value, []byte(strconv.FormatUint(subStatus, 10)), "wrong value for sub status")

found = true
}
}
}
}

require.True(t, found, "sub status not found")
}

// check script doesn't contains errors.
func checkNoErrors(events sdk.Events, t *testing.T) {
for _, event := range events {
if event.Type == types.EventTypeContractStatus {
Expand Down Expand Up @@ -418,3 +464,79 @@ func TestKeeper_ScriptOracle(t *testing.T) {

checkNoErrors(events, t)
}

// Test oracle price return.
func TestKeeper_ErrorScript(t *testing.T) {
config := sdk.GetConfig()
dnodeConfig.InitBechPrefixes(config)

input := setupTestInput(false)

// launch docker
client, compiler, vm := launchDocker(input.dsPort, t)
defer stopDocker(t, client, vm)
defer stopDocker(t, client, compiler)

// create accounts.
addr1 := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
acc1 := input.ak.NewAccountWithAddress(input.ctx, addr1)
coins := sdk.NewCoins(
sdk.NewCoin("dfi", sdk.NewInt(1000000000000000)),
sdk.NewCoin("btc", sdk.NewInt(1)),
)

acc1.SetCoins(coins)
input.ak.SetAccount(input.ctx, acc1)

gs := getGenesis(t)
input.vk.InitGenesis(input.ctx, gs)
input.vk.SetDSContext(input.ctx)
input.vk.StartDSServer(input.ctx)
time.Sleep(2 * time.Second)

// wait for compiler
err := waitStarted(client, compiler.ID, 5*time.Second)
require.NoErrorf(t, err, "can't connect to docker compiler: %v", err)

err = waitStarted(client, vm.ID, 5*time.Second)
require.NoErrorf(t, err, "can't connect to docker vm: %v", err)

// wait reachable compiler
err = waitReachable(*vmCompiler, 5*time.Second)
require.NoErrorf(t, err, "can't connect to compiler server: %v", err)

// wait reachable vm
err = waitReachable(*vmAddress, 5*time.Second)
require.NoErrorf(t, err, "can't connect to vm server: %v", err)

bytecodeScript, err := compilerClient.Compile(*vmCompiler, &vm_grpc.MvIrSourceFile{
Text: errorScript,
Address: common_vm.Bech32ToLibra(addr1),
Type: vm_grpc.ContractType_Script,
})
require.NoErrorf(t, err, "can't get code for error script: %v", err)

args := make([]types.ScriptArg, 1)
args[0] = types.ScriptArg{
Value: strconv.FormatUint(10, 10),
Type: vm_grpc.VMTypeTag_U64,
}

msgScript := types.NewMsgExecuteScript(addr1, bytecodeScript, args)
err = input.vk.ExecuteScript(input.ctx, msgScript)
require.NoError(t, err)

events := input.ctx.EventManager().Events()
require.Contains(t, events, types.NewEventKeep())
for _, e := range events {
printEvent(e, t)
}
checkSubStatus(events, 122, t)

// first of all - check balance
// then check that error still there
// then check that no events there only error and keep status
getAcc := input.ak.GetAccount(input.ctx, addr1)
require.True(t, getAcc.GetCoins().IsEqual(coins))
require.Len(t, events, 2)
}
3 changes: 2 additions & 1 deletion x/vm/internal/keeper/keeper_vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,10 @@ func (keeper Keeper) processExecution(ctx sdk.Context, exec *vm_grpc.VMExecuteRe

if exec.StatusStruct != nil && exec.StatusStruct.MajorStatus != types.VMCodeExecuted {
ctx.EventManager().EmitEvent(types.NewEventError(exec.StatusStruct))
types.PrintVMStackTrace(keeper.Logger(ctx), exec)
return
}

// processing write set.
keeper.processWriteSet(ctx, exec.WriteSet)

for _, vmEvent := range exec.Events {
Expand Down
67 changes: 67 additions & 0 deletions x/vm/internal/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"

"github.com/dfinance/dvm-proto/go/vm_grpc"
"github.com/tendermint/tendermint/libs/log"

"github.com/dfinance/dnode/x/common_vm"
)
Expand Down Expand Up @@ -62,3 +63,69 @@ func VMTypeToStringPanic(tag vm_grpc.VMTypeTag) string {
return val
}
}

// VMWriteOp to string.
func VMWriteOpToString(wOp vm_grpc.VmWriteOp) string {
switch wOp {
case vm_grpc.VmWriteOp_Value:
return "write"

case vm_grpc.VmWriteOp_Deletion:
return "del"

default:
return "unknown"
}
}

// Stake trace.

// Writeset to string.
func WriteSetToString(value *vm_grpc.VMValue) string {
return fmt.Sprintf("%s: \n"+
"\tAddress: %s\n"+
"\tPath: %s\n"+
"\tValue: %s\n",
VMWriteOpToString(value.Type), hex.EncodeToString(value.Path.Address),
hex.EncodeToString(value.Path.Path), hex.EncodeToString(value.Value),
)
}

// Contract status.
func ExecStatusToString(status vm_grpc.ContractStatus, sstruct *vm_grpc.VMStatus) string {
return fmt.Sprintf("Status %s: \n"+
"\tMajor code: %d\n"+
"\tSub status: %d\n"+
"\tMesage: %s\n", status.String(), sstruct.MajorStatus, sstruct.SubStatus, sstruct.Message)
}

// Event to string.
func EventToString(event *vm_grpc.VMEvent) string {
return fmt.Sprintf("Type: %s\n"+
"\tKey: %s\n"+
"\tSequence number: %d\n"+
"\tValue: %s\n",
VMTypeToStringPanic(event.Type.Tag), hex.EncodeToString(event.Key),
event.SequenceNumber, hex.EncodeToString(event.EventData))
}

// Print VM stack trace if contract is not executed successful.
func PrintVMStackTrace(log log.Logger, exec *vm_grpc.VMExecuteResponse) {
stackTrace := "Stack trace: \n"

// print common status
stackTrace += ExecStatusToString(exec.Status, exec.StatusStruct)
stackTrace += "Events\n"

for _, event := range exec.Events {
stackTrace += EventToString(event)
}

// print all write sets
stackTrace += "Write set: \n"
for _, ws := range exec.WriteSet {
stackTrace += WriteSetToString(ws)
}

log.Debug(stackTrace)
}

0 comments on commit ecde272

Please sign in to comment.