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.

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