diff --git a/contract/vm.go b/contract/vm.go index 81e9b43ad..2964ecd26 100644 --- a/contract/vm.go +++ b/contract/vm.go @@ -1348,7 +1348,7 @@ func (ce *executor) vmLoadCode(id []byte) { (*C.char)(unsafe.Pointer(&ce.code[0])), C.size_t(len(ce.code)), hexId, - ce.ctx.service-MaxVmService, + ce.ctx.service-C.int(maxContext), ); cErrMsg != nil { errMsg := C.GoString(cErrMsg) ce.err = errors.New(errMsg) diff --git a/contract/vm_callback.go b/contract/vm_callback.go index 6bc3c79c0..dd2a3a27e 100644 --- a/contract/vm_callback.go +++ b/contract/vm_callback.go @@ -1368,19 +1368,26 @@ func luaCheckView(service C.int) C.int { //export luaCheckTimeout func luaCheckTimeout(service C.int) C.int { - // Temporarily disable timeout check to prevent contract timeout raised from chain service - // if service < BlockFactory { - // service = service + MaxVmService - // } - // if service != BlockFactory { - // return 0 - // } - // select { - // case <-bpTimeout: - // return 1 - // default: - // return 0 - // } + if service < BlockFactory { + // Originally, MaxVmService was used instead of maxContext. service + // value can be 2 and decremented by MaxVmService(=2) during VM loading. + // That means the value of service becomes zero after the latter + // adjustment. + // + // This make the VM check block timeout in a unwanted situation. If that + // happens during the chain service is connecting block, the block chain + // becomes out of sync. + service = service + C.int(maxContext) + } + if service != BlockFactory { + return 0 + } + select { + case <-bpTimeout: + return 1 + default: + return 0 + } return 0 } diff --git a/contract/vm_test.go b/contract/vm_test.go index be28bfcde..9f16fd4e3 100644 --- a/contract/vm_test.go +++ b/contract/vm_test.go @@ -601,103 +601,103 @@ abi.register(infiniteLoop, infiniteCall, catch, contract_catch)` } } -// func TestInfiniteLoopOnPubNet(t *testing.T) { -// bc, err := LoadDummyChain( -// func(d *DummyChain) { -// d.timeout = 50 -// }, -// OnPubNet, -// ) -// if err != nil { -// t.Errorf("failed to create test database: %v", err) -// } -// defer bc.Release() - -// definition := ` -// function infiniteLoop() -// local t = 0 -// while true do -// t = t + 1 -// end -// return t -// end -// function infiniteCall() -// infiniteCall() -// end -// function catch() -// return pcall(infiniteLoop) -// end -// function contract_catch() -// return contract.pcall(infiniteLoop) -// end -// abi.register(infiniteLoop, infiniteCall, catch, contract_catch)` - -// err = bc.ConnectBlock( -// NewLuaTxAccount("ktlee", 100000000000000000), -// NewLuaTxDef("ktlee", "loop", 0, definition), -// ) -// if err != nil { -// t.Error(err) -// } - -// err = bc.ConnectBlock( -// NewLuaTxCall( -// "ktlee", -// "loop", -// 0, -// `{"Name":"infiniteLoop"}`, -// ), -// ) -// errTimeout := VmTimeoutError{} -// if err == nil { -// t.Errorf("expected: %v", errTimeout) -// } -// if err != nil && !strings.Contains(err.Error(), errTimeout.Error()) { -// t.Error(err) -// } - -// err = bc.ConnectBlock( -// NewLuaTxCall( -// "ktlee", -// "loop", -// 0, -// `{"Name":"catch"}`, -// ), -// ) -// if err == nil { -// t.Errorf("expected: %v", errTimeout) -// } -// if err != nil && !strings.Contains(err.Error(), errTimeout.Error()) { -// t.Error(err) -// } - -// err = bc.ConnectBlock( -// NewLuaTxCall( -// "ktlee", -// "loop", -// 0, -// `{"Name":"contract_catch"}`, -// ), -// ) -// if err == nil { -// t.Errorf("expected: %v", errTimeout) -// } -// if err != nil && !strings.Contains(err.Error(), errTimeout.Error()) { -// t.Error(err) -// } - -// err = bc.ConnectBlock( -// NewLuaTxCall( -// "ktlee", -// "loop", -// 0, -// `{"Name":"infiniteCall"}`, -// ).Fail("stack overflow"), -// ) -// if err != nil { -// t.Error(err) -// } -// } +func TestInfiniteLoopOnPubNet(t *testing.T) { + bc, err := LoadDummyChain( + func(d *DummyChain) { + d.timeout = 50 + }, + OnPubNet, + ) + if err != nil { + t.Errorf("failed to create test database: %v", err) + } + defer bc.Release() + + definition := ` +function infiniteLoop() + local t = 0 + while true do + t = t + 1 + end + return t +end +function infiniteCall() + infiniteCall() +end +function catch() + return pcall(infiniteLoop) +end +function contract_catch() + return contract.pcall(infiniteLoop) +end +abi.register(infiniteLoop, infiniteCall, catch, contract_catch)` + + err = bc.ConnectBlock( + NewLuaTxAccount("ktlee", 100000000000000000), + NewLuaTxDef("ktlee", "loop", 0, definition), + ) + if err != nil { + t.Error(err) + } + + err = bc.ConnectBlock( + NewLuaTxCall( + "ktlee", + "loop", + 0, + `{"Name":"infiniteLoop"}`, + ), + ) + errTimeout := VmTimeoutError{} + if err == nil { + t.Errorf("expected: %v", errTimeout) + } + if err != nil && !strings.Contains(err.Error(), errTimeout.Error()) { + t.Error(err) + } + + err = bc.ConnectBlock( + NewLuaTxCall( + "ktlee", + "loop", + 0, + `{"Name":"catch"}`, + ), + ) + if err == nil { + t.Errorf("expected: %v", errTimeout) + } + if err != nil && !strings.Contains(err.Error(), errTimeout.Error()) { + t.Error(err) + } + + err = bc.ConnectBlock( + NewLuaTxCall( + "ktlee", + "loop", + 0, + `{"Name":"contract_catch"}`, + ), + ) + if err == nil { + t.Errorf("expected: %v", errTimeout) + } + if err != nil && !strings.Contains(err.Error(), errTimeout.Error()) { + t.Error(err) + } + + err = bc.ConnectBlock( + NewLuaTxCall( + "ktlee", + "loop", + 0, + `{"Name":"infiniteCall"}`, + ).Fail("stack overflow"), + ) + if err != nil { + t.Error(err) + } +} func TestUpdateSize(t *testing.T) { bc, err := LoadDummyChain() @@ -5365,73 +5365,73 @@ abi.register(testall) } } -// func TestTimeoutCnt(t *testing.T) { -// timeout := 500 -// src := ` -// function infinite_loop(n) -// while true do -// end -// return 0 -// end - -// abi.register(infinite_loop) -// ` -// bc, err := LoadDummyChain( -// func(d *DummyChain) { -// d.timeout = timeout // milliseconds -// }, -// OnPubNet, -// ) -// if err != nil { -// t.Errorf("failed to create test database: %v", err) -// } -// defer bc.Release() - -// err = bc.ConnectBlock( -// NewLuaTxAccount("ktlee", 100000000000000000), -// NewLuaTxDef("ktlee", "timeout-cnt", 0, src), -// ) -// if err != nil { -// t.Error(err) -// } -// err = bc.ConnectBlock( -// NewLuaTxCall("ktlee", "timeout-cnt", 0, `{"Name": "infinite_loop"}`).Fail("contract timeout"), -// ) -// if err != nil { -// t.Error(err) -// } -// err = bc.Query("timeout-cnt", `{"Name": "infinite_loop"}`, "exceeded the maximum instruction count") -// if err != nil { -// t.Error(err) -// } - -// src2 := ` -// function a() -// src = [[ -// while true do -// end -// function b() -// end -// abi.register(b) -// ]] -// contract.deploy(src) -// end - -// abi.register(a) -// ` -// err = bc.ConnectBlock( -// NewLuaTxDef("ktlee", "timeout-cnt2", 0, src2), -// ) -// if err != nil { -// t.Error(err) -// } -// err = bc.ConnectBlock( -// NewLuaTxCall("ktlee", "timeout-cnt2", 0, `{"Name": "a"}`).Fail("contract timeout"), -// ) -// if err != nil { -// t.Error(err) -// } -// } +func TestTimeoutCnt(t *testing.T) { + timeout := 500 + src := ` +function infinite_loop(n) + while true do + end + return 0 +end + +abi.register(infinite_loop) +` + bc, err := LoadDummyChain( + func(d *DummyChain) { + d.timeout = timeout // milliseconds + }, + OnPubNet, + ) + if err != nil { + t.Errorf("failed to create test database: %v", err) + } + defer bc.Release() + + err = bc.ConnectBlock( + NewLuaTxAccount("ktlee", 100000000000000000), + NewLuaTxDef("ktlee", "timeout-cnt", 0, src), + ) + if err != nil { + t.Error(err) + } + err = bc.ConnectBlock( + NewLuaTxCall("ktlee", "timeout-cnt", 0, `{"Name": "infinite_loop"}`).Fail("contract timeout"), + ) + if err != nil { + t.Error(err) + } + err = bc.Query("timeout-cnt", `{"Name": "infinite_loop"}`, "exceeded the maximum instruction count") + if err != nil { + t.Error(err) + } + + src2 := ` +function a() + src = [[ +while true do +end + function b() + end + abi.register(b) + ]] + contract.deploy(src) +end + +abi.register(a) +` + err = bc.ConnectBlock( + NewLuaTxDef("ktlee", "timeout-cnt2", 0, src2), + ) + if err != nil { + t.Error(err) + } + err = bc.ConnectBlock( + NewLuaTxCall("ktlee", "timeout-cnt2", 0, `{"Name": "a"}`).Fail("contract timeout"), + ) + if err != nil { + t.Error(err) + } +} func TestFeeDelegation(t *testing.T) { definition := `