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.7 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. for i := 0; i < len(N); i++ {
  49. code = append(code, N[i])
  50. }
  51. 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}...)
  52. start := time.Now()
  53. output, err := ourVm.Call(account1, account2, code, []byte{}, 0, &gas)
  54. fmt.Printf("Output: %v Error: %v\n", output, err)
  55. fmt.Println("Call took:", time.Since(start))
  56. if err != nil {
  57. t.Fatal(err)
  58. }
  59. }
  60. // Tests the code for a subcurrency contract compiled by serpent
  61. func TestSubcurrency(t *testing.T) {
  62. st := newAppState()
  63. // Create accounts
  64. account1 := &Account{
  65. Address: LeftPadWord256(makeBytes(20)),
  66. }
  67. account2 := &Account{
  68. Address: LeftPadWord256(makeBytes(20)),
  69. }
  70. st.accounts[account1.Address.String()] = account1
  71. st.accounts[account2.Address.String()] = account2
  72. ourVm := NewVM(st, newParams(), Zero256, nil)
  73. var gas int64 = 1000
  74. code_parts := []string{"620f42403355",
  75. "7c0100000000000000000000000000000000000000000000000000000000",
  76. "600035046315cf268481141561004657",
  77. "6004356040526040515460605260206060f35b63693200ce81141561008757",
  78. "60043560805260243560a052335460c0523360e05260a05160c05112151561008657",
  79. "60a05160c0510360e0515560a0516080515401608051555b5b505b6000f3"}
  80. code, _ := hex.DecodeString(strings.Join(code_parts, ""))
  81. fmt.Printf("Code: %x\n", code)
  82. data, _ := hex.DecodeString("693200CE0000000000000000000000004B4363CDE27C2EB05E66357DB05BC5C88F850C1A0000000000000000000000000000000000000000000000000000000000000005")
  83. output, err := ourVm.Call(account1, account2, code, data, 0, &gas)
  84. fmt.Printf("Output: %v Error: %v\n", output, err)
  85. if err != nil {
  86. t.Fatal(err)
  87. }
  88. }
  89. // Test sending tokens from a contract to another account
  90. func TestSendCall(t *testing.T) {
  91. fakeAppState := newAppState()
  92. ourVm := NewVM(fakeAppState, newParams(), Zero256, nil)
  93. // Create accounts
  94. account1 := &Account{
  95. Address: Int64ToWord256(100),
  96. }
  97. account2 := &Account{
  98. Address: Int64ToWord256(101),
  99. }
  100. account3 := &Account{
  101. Address: Int64ToWord256(102),
  102. }
  103. // account1 will call account2 which will trigger CALL opcode to account3
  104. addr := account3.Address.Postfix(20)
  105. contractCode := callContractCode(addr)
  106. //----------------------------------------------
  107. // account2 has insufficient balance, should fail
  108. exception := runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 1000)
  109. if exception == "" {
  110. t.Fatal("Expected exception")
  111. }
  112. //----------------------------------------------
  113. // give account2 sufficient balance, should pass
  114. account2.Balance = 100000
  115. exception = runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 1000)
  116. if exception != "" {
  117. t.Fatal("Unexpected exception", exception)
  118. }
  119. //----------------------------------------------
  120. // insufficient gas, should fail
  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. */