You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
5.8 KiB

  1. package vm
  2. import (
  3. "crypto/rand"
  4. "encoding/hex"
  5. "fmt"
  6. "strings"
  7. "testing"
  8. "time"
  9. . "github.com/tendermint/tendermint/common"
  10. "github.com/tendermint/tendermint/events"
  11. "github.com/tendermint/tendermint/types"
  12. . "github.com/tendermint/tendermint/vm"
  13. )
  14. func newAppState() *FakeAppState {
  15. return &FakeAppState{
  16. accounts: make(map[string]*Account),
  17. storage: make(map[string]Word256),
  18. logs: nil,
  19. }
  20. }
  21. func newParams() Params {
  22. return Params{
  23. BlockHeight: 0,
  24. BlockHash: Zero256,
  25. BlockTime: 0,
  26. GasLimit: 0,
  27. }
  28. }
  29. func makeBytes(n int) []byte {
  30. b := make([]byte, n)
  31. rand.Read(b)
  32. return b
  33. }
  34. // Runs a basic loop
  35. func TestVM(t *testing.T) {
  36. ourVm := NewVM(newAppState(), newParams(), Zero256, nil)
  37. // Create accounts
  38. account1 := &Account{
  39. Address: Int64ToWord256(100),
  40. }
  41. account2 := &Account{
  42. Address: Int64ToWord256(101),
  43. }
  44. var gas int64 = 100000
  45. N := []byte{0x0f, 0x0f}
  46. // Loop N times
  47. code := []byte{0x60, 0x00, 0x60, 0x20, 0x52, 0x5B, byte(0x60 + len(N) - 1)}
  48. code = append(code, N...)
  49. code = append(code, []byte{0x60, 0x20, 0x51, 0x12, 0x15, 0x60, byte(0x1b + len(N)), 0x57, 0x60, 0x01, 0x60, 0x20, 0x51, 0x01, 0x60, 0x20, 0x52, 0x60, 0x05, 0x56, 0x5B}...)
  50. start := time.Now()
  51. output, err := ourVm.Call(account1, account2, code, []byte{}, 0, &gas)
  52. fmt.Printf("Output: %v Error: %v\n", output, err)
  53. fmt.Println("Call took:", time.Since(start))
  54. if err != nil {
  55. t.Fatal(err)
  56. }
  57. }
  58. // Tests the code for a subcurrency contract compiled by serpent
  59. func TestSubcurrency(t *testing.T) {
  60. st := newAppState()
  61. // Create accounts
  62. account1 := &Account{
  63. Address: LeftPadWord256(makeBytes(20)),
  64. }
  65. account2 := &Account{
  66. Address: LeftPadWord256(makeBytes(20)),
  67. }
  68. st.accounts[account1.Address.String()] = account1
  69. st.accounts[account2.Address.String()] = account2
  70. ourVm := NewVM(st, newParams(), Zero256, nil)
  71. var gas int64 = 1000
  72. code_parts := []string{"620f42403355",
  73. "7c0100000000000000000000000000000000000000000000000000000000",
  74. "600035046315cf268481141561004657",
  75. "6004356040526040515460605260206060f35b63693200ce81141561008757",
  76. "60043560805260243560a052335460c0523360e05260a05160c05112151561008657",
  77. "60a05160c0510360e0515560a0516080515401608051555b5b505b6000f3"}
  78. code, _ := hex.DecodeString(strings.Join(code_parts, ""))
  79. fmt.Printf("Code: %x\n", code)
  80. data, _ := hex.DecodeString("693200CE0000000000000000000000004B4363CDE27C2EB05E66357DB05BC5C88F850C1A0000000000000000000000000000000000000000000000000000000000000005")
  81. output, err := ourVm.Call(account1, account2, code, data, 0, &gas)
  82. fmt.Printf("Output: %v Error: %v\n", output, err)
  83. if err != nil {
  84. t.Fatal(err)
  85. }
  86. }
  87. // Test sending tokens from a contract to another account
  88. func TestSendCall(t *testing.T) {
  89. fakeAppState := newAppState()
  90. ourVm := NewVM(fakeAppState, newParams(), Zero256, nil)
  91. // Create accounts
  92. account1 := &Account{
  93. Address: Int64ToWord256(100),
  94. }
  95. account2 := &Account{
  96. Address: Int64ToWord256(101),
  97. }
  98. account3 := &Account{
  99. Address: Int64ToWord256(102),
  100. }
  101. // account1 will call account2 which will trigger CALL opcode to account3
  102. addr := account3.Address.Postfix(20)
  103. contractCode := callContractCode(addr)
  104. //----------------------------------------------
  105. // account2 has insufficient balance, should fail
  106. fmt.Println("Should fail with insufficient balance")
  107. exception := runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 1000)
  108. if exception == "" {
  109. t.Fatal("Expected exception")
  110. }
  111. //----------------------------------------------
  112. // give account2 sufficient balance, should pass
  113. account2.Balance = 100000
  114. exception = runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 1000)
  115. if exception != "" {
  116. t.Fatal("Unexpected exception", exception)
  117. }
  118. //----------------------------------------------
  119. // insufficient gas, should fail
  120. fmt.Println("Should fail with insufficient gas")
  121. account2.Balance = 100000
  122. exception = runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 100)
  123. if exception == "" {
  124. t.Fatal("Expected exception")
  125. }
  126. }
  127. // subscribes to an AccReceive, runs the vm, returns the exception
  128. func runVMWaitEvents(t *testing.T, ourVm *VM, caller, callee *Account, subscribeAddr, contractCode []byte, gas int64) string {
  129. // we need to catch the event from the CALL to check for exceptions
  130. evsw := new(events.EventSwitch)
  131. evsw.Start()
  132. ch := make(chan interface{})
  133. fmt.Printf("subscribe to %x\n", subscribeAddr)
  134. evsw.AddListenerForEvent("test", types.EventStringAccReceive(subscribeAddr), func(msg interface{}) {
  135. ch <- msg
  136. })
  137. evc := events.NewEventCache(evsw)
  138. ourVm.SetFireable(evc)
  139. go func() {
  140. start := time.Now()
  141. output, err := ourVm.Call(caller, callee, contractCode, []byte{}, 0, &gas)
  142. fmt.Printf("Output: %v Error: %v\n", output, err)
  143. fmt.Println("Call took:", time.Since(start))
  144. if err != nil {
  145. ch <- err.Error()
  146. }
  147. evc.Flush()
  148. }()
  149. msg := <-ch
  150. switch ev := msg.(type) {
  151. case types.EventMsgCallTx:
  152. return ev.Exception
  153. case types.EventMsgCall:
  154. return ev.Exception
  155. case string:
  156. return ev
  157. }
  158. return ""
  159. }
  160. // this is code to call another contract (hardcoded as addr)
  161. func callContractCode(addr []byte) []byte {
  162. gas1, gas2 := byte(0x1), byte(0x1)
  163. value := byte(0x69)
  164. inOff, inSize := byte(0x0), byte(0x0) // no call data
  165. retOff, retSize := byte(0x0), byte(0x20)
  166. // this is the code we want to run (send funds to an account and return)
  167. contractCode := []byte{0x60, retSize, 0x60, retOff, 0x60, inSize, 0x60, inOff, 0x60, value, 0x73}
  168. contractCode = append(contractCode, addr...)
  169. contractCode = append(contractCode, []byte{0x61, gas1, gas2, 0xf1, 0x60, 0x20, 0x60, 0x0, 0xf3}...)
  170. return contractCode
  171. }
  172. /*
  173. // infinite loop
  174. code := []byte{0x5B, 0x60, 0x00, 0x56}
  175. // mstore
  176. code := []byte{0x60, 0x00, 0x60, 0x20}
  177. // mstore, mload
  178. code := []byte{0x60, 0x01, 0x60, 0x20, 0x52, 0x60, 0x20, 0x51}
  179. */