rules_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. // Copyright 2018 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // go-ethereum is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. //
  17. package rules
  18. import (
  19. "fmt"
  20. "math/big"
  21. "strings"
  22. "testing"
  23. "github.com/ethereum/go-ethereum/accounts"
  24. "github.com/ethereum/go-ethereum/common"
  25. "github.com/ethereum/go-ethereum/common/hexutil"
  26. "github.com/ethereum/go-ethereum/core/types"
  27. "github.com/ethereum/go-ethereum/internal/ethapi"
  28. "github.com/ethereum/go-ethereum/signer/core"
  29. "github.com/ethereum/go-ethereum/signer/storage"
  30. )
  31. const JS = `
  32. /**
  33. This is an example implementation of a Javascript rule file.
  34. When the signer receives a request over the external API, the corresponding method is evaluated.
  35. Three things can happen:
  36. 1. The method returns "Approve". This means the operation is permitted.
  37. 2. The method returns "Reject". This means the operation is rejected.
  38. 3. Anything else; other return values [*], method not implemented or exception occurred during processing. This means
  39. that the operation will continue to manual processing, via the regular UI method chosen by the user.
  40. [*] Note: Future version of the ruleset may use more complex json-based returnvalues, making it possible to not
  41. only respond Approve/Reject/Manual, but also modify responses. For example, choose to list only one, but not all
  42. accounts in a list-request. The points above will continue to hold for non-json based responses ("Approve"/"Reject").
  43. **/
  44. function ApproveListing(request){
  45. console.log("In js approve listing");
  46. console.log(request.accounts[3].Address)
  47. console.log(request.meta.Remote)
  48. return "Approve"
  49. }
  50. function ApproveTx(request){
  51. console.log("test");
  52. console.log("from");
  53. return "Reject";
  54. }
  55. function test(thing){
  56. console.log(thing.String())
  57. }
  58. `
  59. func mixAddr(a string) (*common.MixedcaseAddress, error) {
  60. return common.NewMixedcaseAddressFromString(a)
  61. }
  62. type alwaysDenyUI struct{}
  63. func (alwaysDenyUI) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) {
  64. return core.UserInputResponse{}, nil
  65. }
  66. func (alwaysDenyUI) OnSignerStartup(info core.StartupInfo) {
  67. }
  68. func (alwaysDenyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
  69. return core.SignTxResponse{Transaction: request.Transaction, Approved: false, Password: ""}, nil
  70. }
  71. func (alwaysDenyUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
  72. return core.SignDataResponse{Approved: false, Password: ""}, nil
  73. }
  74. func (alwaysDenyUI) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) {
  75. return core.ExportResponse{Approved: false}, nil
  76. }
  77. func (alwaysDenyUI) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) {
  78. return core.ImportResponse{Approved: false, OldPassword: "", NewPassword: ""}, nil
  79. }
  80. func (alwaysDenyUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
  81. return core.ListResponse{Accounts: nil}, nil
  82. }
  83. func (alwaysDenyUI) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
  84. return core.NewAccountResponse{Approved: false, Password: ""}, nil
  85. }
  86. func (alwaysDenyUI) ShowError(message string) {
  87. panic("implement me")
  88. }
  89. func (alwaysDenyUI) ShowInfo(message string) {
  90. panic("implement me")
  91. }
  92. func (alwaysDenyUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
  93. panic("implement me")
  94. }
  95. func initRuleEngine(js string) (*rulesetUI, error) {
  96. r, err := NewRuleEvaluator(&alwaysDenyUI{}, storage.NewEphemeralStorage(), storage.NewEphemeralStorage())
  97. if err != nil {
  98. return nil, fmt.Errorf("failed to create js engine: %v", err)
  99. }
  100. if err = r.Init(js); err != nil {
  101. return nil, fmt.Errorf("failed to load bootstrap js: %v", err)
  102. }
  103. return r, nil
  104. }
  105. func TestListRequest(t *testing.T) {
  106. accs := make([]core.Account, 5)
  107. for i := range accs {
  108. addr := fmt.Sprintf("000000000000000000000000000000000000000%x", i)
  109. acc := core.Account{
  110. Address: common.BytesToAddress(common.Hex2Bytes(addr)),
  111. URL: accounts.URL{Scheme: "test", Path: fmt.Sprintf("acc-%d", i)},
  112. }
  113. accs[i] = acc
  114. }
  115. js := `function ApproveListing(){ return "Approve" }`
  116. r, err := initRuleEngine(js)
  117. if err != nil {
  118. t.Errorf("Couldn't create evaluator %v", err)
  119. return
  120. }
  121. resp, err := r.ApproveListing(&core.ListRequest{
  122. Accounts: accs,
  123. Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"},
  124. })
  125. if len(resp.Accounts) != len(accs) {
  126. t.Errorf("Expected check to resolve to 'Approve'")
  127. }
  128. }
  129. func TestSignTxRequest(t *testing.T) {
  130. js := `
  131. function ApproveTx(r){
  132. console.log("transaction.from", r.transaction.from);
  133. console.log("transaction.to", r.transaction.to);
  134. console.log("transaction.value", r.transaction.value);
  135. console.log("transaction.nonce", r.transaction.nonce);
  136. if(r.transaction.from.toLowerCase()=="0x0000000000000000000000000000000000001337"){ return "Approve"}
  137. if(r.transaction.from.toLowerCase()=="0x000000000000000000000000000000000000dead"){ return "Reject"}
  138. }`
  139. r, err := initRuleEngine(js)
  140. if err != nil {
  141. t.Errorf("Couldn't create evaluator %v", err)
  142. return
  143. }
  144. to, err := mixAddr("000000000000000000000000000000000000dead")
  145. if err != nil {
  146. t.Error(err)
  147. return
  148. }
  149. from, err := mixAddr("0000000000000000000000000000000000001337")
  150. if err != nil {
  151. t.Error(err)
  152. return
  153. }
  154. fmt.Printf("to %v", to.Address().String())
  155. resp, err := r.ApproveTx(&core.SignTxRequest{
  156. Transaction: core.SendTxArgs{
  157. From: *from,
  158. To: to},
  159. Callinfo: nil,
  160. Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"},
  161. })
  162. if err != nil {
  163. t.Errorf("Unexpected error %v", err)
  164. }
  165. if !resp.Approved {
  166. t.Errorf("Expected check to resolve to 'Approve'")
  167. }
  168. }
  169. type dummyUI struct {
  170. calls []string
  171. }
  172. func (d *dummyUI) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) {
  173. d.calls = append(d.calls, "OnInputRequired")
  174. return core.UserInputResponse{}, nil
  175. }
  176. func (d *dummyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
  177. d.calls = append(d.calls, "ApproveTx")
  178. return core.SignTxResponse{}, core.ErrRequestDenied
  179. }
  180. func (d *dummyUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
  181. d.calls = append(d.calls, "ApproveSignData")
  182. return core.SignDataResponse{}, core.ErrRequestDenied
  183. }
  184. func (d *dummyUI) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) {
  185. d.calls = append(d.calls, "ApproveExport")
  186. return core.ExportResponse{}, core.ErrRequestDenied
  187. }
  188. func (d *dummyUI) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) {
  189. d.calls = append(d.calls, "ApproveImport")
  190. return core.ImportResponse{}, core.ErrRequestDenied
  191. }
  192. func (d *dummyUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
  193. d.calls = append(d.calls, "ApproveListing")
  194. return core.ListResponse{}, core.ErrRequestDenied
  195. }
  196. func (d *dummyUI) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
  197. d.calls = append(d.calls, "ApproveNewAccount")
  198. return core.NewAccountResponse{}, core.ErrRequestDenied
  199. }
  200. func (d *dummyUI) ShowError(message string) {
  201. d.calls = append(d.calls, "ShowError")
  202. }
  203. func (d *dummyUI) ShowInfo(message string) {
  204. d.calls = append(d.calls, "ShowInfo")
  205. }
  206. func (d *dummyUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
  207. d.calls = append(d.calls, "OnApprovedTx")
  208. }
  209. func (d *dummyUI) OnSignerStartup(info core.StartupInfo) {
  210. }
  211. //TestForwarding tests that the rule-engine correctly dispatches requests to the next caller
  212. func TestForwarding(t *testing.T) {
  213. js := ""
  214. ui := &dummyUI{make([]string, 0)}
  215. jsBackend := storage.NewEphemeralStorage()
  216. credBackend := storage.NewEphemeralStorage()
  217. r, err := NewRuleEvaluator(ui, jsBackend, credBackend)
  218. if err != nil {
  219. t.Fatalf("Failed to create js engine: %v", err)
  220. }
  221. if err = r.Init(js); err != nil {
  222. t.Fatalf("Failed to load bootstrap js: %v", err)
  223. }
  224. r.ApproveSignData(nil)
  225. r.ApproveTx(nil)
  226. r.ApproveImport(nil)
  227. r.ApproveNewAccount(nil)
  228. r.ApproveListing(nil)
  229. r.ApproveExport(nil)
  230. r.ShowError("test")
  231. r.ShowInfo("test")
  232. //This one is not forwarded
  233. r.OnApprovedTx(ethapi.SignTransactionResult{})
  234. expCalls := 8
  235. if len(ui.calls) != expCalls {
  236. t.Errorf("Expected %d forwarded calls, got %d: %s", expCalls, len(ui.calls), strings.Join(ui.calls, ","))
  237. }
  238. }
  239. func TestMissingFunc(t *testing.T) {
  240. r, err := initRuleEngine(JS)
  241. if err != nil {
  242. t.Errorf("Couldn't create evaluator %v", err)
  243. return
  244. }
  245. _, err = r.execute("MissingMethod", "test")
  246. if err == nil {
  247. t.Error("Expected error")
  248. }
  249. approved, err := r.checkApproval("MissingMethod", nil, nil)
  250. if err == nil {
  251. t.Errorf("Expected missing method to yield error'")
  252. }
  253. if approved {
  254. t.Errorf("Expected missing method to cause non-approval")
  255. }
  256. fmt.Printf("Err %v", err)
  257. }
  258. func TestStorage(t *testing.T) {
  259. js := `
  260. function testStorage(){
  261. storage.Put("mykey", "myvalue")
  262. a = storage.Get("mykey")
  263. storage.Put("mykey", ["a", "list"]) // Should result in "a,list"
  264. a += storage.Get("mykey")
  265. storage.Put("mykey", {"an": "object"}) // Should result in "[object Object]"
  266. a += storage.Get("mykey")
  267. storage.Put("mykey", JSON.stringify({"an": "object"})) // Should result in '{"an":"object"}'
  268. a += storage.Get("mykey")
  269. a += storage.Get("missingkey") //Missing keys should result in empty string
  270. storage.Put("","missing key==noop") // Can't store with 0-length key
  271. a += storage.Get("") // Should result in ''
  272. var b = new BigNumber(2)
  273. var c = new BigNumber(16)//"0xf0",16)
  274. var d = b.plus(c)
  275. console.log(d)
  276. return a
  277. }
  278. `
  279. r, err := initRuleEngine(js)
  280. if err != nil {
  281. t.Errorf("Couldn't create evaluator %v", err)
  282. return
  283. }
  284. v, err := r.execute("testStorage", nil)
  285. if err != nil {
  286. t.Errorf("Unexpected error %v", err)
  287. }
  288. retval, err := v.ToString()
  289. if err != nil {
  290. t.Errorf("Unexpected error %v", err)
  291. }
  292. exp := `myvaluea,list[object Object]{"an":"object"}`
  293. if retval != exp {
  294. t.Errorf("Unexpected data, expected '%v', got '%v'", exp, retval)
  295. }
  296. fmt.Printf("Err %v", err)
  297. }
  298. const ExampleTxWindow = `
  299. function big(str){
  300. if(str.slice(0,2) == "0x"){ return new BigNumber(str.slice(2),16)}
  301. return new BigNumber(str)
  302. }
  303. // Time window: 1 week
  304. var window = 1000* 3600*24*7;
  305. // Limit : 1 ether
  306. var limit = new BigNumber("1e18");
  307. function isLimitOk(transaction){
  308. var value = big(transaction.value)
  309. // Start of our window function
  310. var windowstart = new Date().getTime() - window;
  311. var txs = [];
  312. var stored = storage.Get('txs');
  313. if(stored != ""){
  314. txs = JSON.parse(stored)
  315. }
  316. // First, remove all that have passed out of the time-window
  317. var newtxs = txs.filter(function(tx){return tx.tstamp > windowstart});
  318. console.log(txs, newtxs.length);
  319. // Secondly, aggregate the current sum
  320. sum = new BigNumber(0)
  321. sum = newtxs.reduce(function(agg, tx){ return big(tx.value).plus(agg)}, sum);
  322. console.log("ApproveTx > Sum so far", sum);
  323. console.log("ApproveTx > Requested", value.toNumber());
  324. // Would we exceed weekly limit ?
  325. return sum.plus(value).lt(limit)
  326. }
  327. function ApproveTx(r){
  328. console.log(r)
  329. console.log(typeof(r))
  330. if (isLimitOk(r.transaction)){
  331. return "Approve"
  332. }
  333. return "Nope"
  334. }
  335. /**
  336. * OnApprovedTx(str) is called when a transaction has been approved and signed. The parameter
  337. * 'response_str' contains the return value that will be sent to the external caller.
  338. * The return value from this method is ignore - the reason for having this callback is to allow the
  339. * ruleset to keep track of approved transactions.
  340. *
  341. * When implementing rate-limited rules, this callback should be used.
  342. * If a rule responds with neither 'Approve' nor 'Reject' - the tx goes to manual processing. If the user
  343. * then accepts the transaction, this method will be called.
  344. *
  345. * TLDR; Use this method to keep track of signed transactions, instead of using the data in ApproveTx.
  346. */
  347. function OnApprovedTx(resp){
  348. var value = big(resp.tx.value)
  349. var txs = []
  350. // Load stored transactions
  351. var stored = storage.Get('txs');
  352. if(stored != ""){
  353. txs = JSON.parse(stored)
  354. }
  355. // Add this to the storage
  356. txs.push({tstamp: new Date().getTime(), value: value});
  357. storage.Put("txs", JSON.stringify(txs));
  358. }
  359. `
  360. func dummyTx(value hexutil.Big) *core.SignTxRequest {
  361. to, _ := mixAddr("000000000000000000000000000000000000dead")
  362. from, _ := mixAddr("000000000000000000000000000000000000dead")
  363. n := hexutil.Uint64(3)
  364. gas := hexutil.Uint64(21000)
  365. gasPrice := hexutil.Big(*big.NewInt(2000000))
  366. return &core.SignTxRequest{
  367. Transaction: core.SendTxArgs{
  368. From: *from,
  369. To: to,
  370. Value: value,
  371. Nonce: n,
  372. GasPrice: gasPrice,
  373. Gas: gas,
  374. },
  375. Callinfo: []core.ValidationInfo{
  376. {Typ: "Warning", Message: "All your base are bellong to us"},
  377. },
  378. Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"},
  379. }
  380. }
  381. func dummyTxWithV(value uint64) *core.SignTxRequest {
  382. v := big.NewInt(0).SetUint64(value)
  383. h := hexutil.Big(*v)
  384. return dummyTx(h)
  385. }
  386. func dummySigned(value *big.Int) *types.Transaction {
  387. to := common.HexToAddress("000000000000000000000000000000000000dead")
  388. gas := uint64(21000)
  389. gasPrice := big.NewInt(2000000)
  390. data := make([]byte, 0)
  391. return types.NewTransaction(3, to, value, gas, gasPrice, data)
  392. }
  393. func TestLimitWindow(t *testing.T) {
  394. r, err := initRuleEngine(ExampleTxWindow)
  395. if err != nil {
  396. t.Errorf("Couldn't create evaluator %v", err)
  397. return
  398. }
  399. // 0.3 ether: 429D069189E0000 wei
  400. v := big.NewInt(0).SetBytes(common.Hex2Bytes("0429D069189E0000"))
  401. h := hexutil.Big(*v)
  402. // The first three should succeed
  403. for i := 0; i < 3; i++ {
  404. unsigned := dummyTx(h)
  405. resp, err := r.ApproveTx(unsigned)
  406. if err != nil {
  407. t.Errorf("Unexpected error %v", err)
  408. }
  409. if !resp.Approved {
  410. t.Errorf("Expected check to resolve to 'Approve'")
  411. }
  412. // Create a dummy signed transaction
  413. response := ethapi.SignTransactionResult{
  414. Tx: dummySigned(v),
  415. Raw: common.Hex2Bytes("deadbeef"),
  416. }
  417. r.OnApprovedTx(response)
  418. }
  419. // Fourth should fail
  420. resp, err := r.ApproveTx(dummyTx(h))
  421. if resp.Approved {
  422. t.Errorf("Expected check to resolve to 'Reject'")
  423. }
  424. }
  425. // dontCallMe is used as a next-handler that does not want to be called - it invokes test failure
  426. type dontCallMe struct {
  427. t *testing.T
  428. }
  429. func (d *dontCallMe) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) {
  430. d.t.Fatalf("Did not expect next-handler to be called")
  431. return core.UserInputResponse{}, nil
  432. }
  433. func (d *dontCallMe) OnSignerStartup(info core.StartupInfo) {
  434. }
  435. func (d *dontCallMe) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
  436. d.t.Fatalf("Did not expect next-handler to be called")
  437. return core.SignTxResponse{}, core.ErrRequestDenied
  438. }
  439. func (d *dontCallMe) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
  440. d.t.Fatalf("Did not expect next-handler to be called")
  441. return core.SignDataResponse{}, core.ErrRequestDenied
  442. }
  443. func (d *dontCallMe) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) {
  444. d.t.Fatalf("Did not expect next-handler to be called")
  445. return core.ExportResponse{}, core.ErrRequestDenied
  446. }
  447. func (d *dontCallMe) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) {
  448. d.t.Fatalf("Did not expect next-handler to be called")
  449. return core.ImportResponse{}, core.ErrRequestDenied
  450. }
  451. func (d *dontCallMe) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
  452. d.t.Fatalf("Did not expect next-handler to be called")
  453. return core.ListResponse{}, core.ErrRequestDenied
  454. }
  455. func (d *dontCallMe) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
  456. d.t.Fatalf("Did not expect next-handler to be called")
  457. return core.NewAccountResponse{}, core.ErrRequestDenied
  458. }
  459. func (d *dontCallMe) ShowError(message string) {
  460. d.t.Fatalf("Did not expect next-handler to be called")
  461. }
  462. func (d *dontCallMe) ShowInfo(message string) {
  463. d.t.Fatalf("Did not expect next-handler to be called")
  464. }
  465. func (d *dontCallMe) OnApprovedTx(tx ethapi.SignTransactionResult) {
  466. d.t.Fatalf("Did not expect next-handler to be called")
  467. }
  468. //TestContextIsCleared tests that the rule-engine does not retain variables over several requests.
  469. // if it does, that would be bad since developers may rely on that to store data,
  470. // instead of using the disk-based data storage
  471. func TestContextIsCleared(t *testing.T) {
  472. js := `
  473. function ApproveTx(){
  474. if (typeof foobar == 'undefined') {
  475. foobar = "Approve"
  476. }
  477. console.log(foobar)
  478. if (foobar == "Approve"){
  479. foobar = "Reject"
  480. }else{
  481. foobar = "Approve"
  482. }
  483. return foobar
  484. }
  485. `
  486. ui := &dontCallMe{t}
  487. r, err := NewRuleEvaluator(ui, storage.NewEphemeralStorage(), storage.NewEphemeralStorage())
  488. if err != nil {
  489. t.Fatalf("Failed to create js engine: %v", err)
  490. }
  491. if err = r.Init(js); err != nil {
  492. t.Fatalf("Failed to load bootstrap js: %v", err)
  493. }
  494. tx := dummyTxWithV(0)
  495. r1, err := r.ApproveTx(tx)
  496. r2, err := r.ApproveTx(tx)
  497. if r1.Approved != r2.Approved {
  498. t.Errorf("Expected execution context to be cleared between executions")
  499. }
  500. }
  501. func TestSignData(t *testing.T) {
  502. js := `function ApproveListing(){
  503. return "Approve"
  504. }
  505. function ApproveSignData(r){
  506. if( r.address.toLowerCase() == "0x694267f14675d7e1b9494fd8d72fefe1755710fa")
  507. {
  508. if(r.message.indexOf("bazonk") >= 0){
  509. return "Approve"
  510. }
  511. return "Reject"
  512. }
  513. // Otherwise goes to manual processing
  514. }`
  515. r, err := initRuleEngine(js)
  516. if err != nil {
  517. t.Errorf("Couldn't create evaluator %v", err)
  518. return
  519. }
  520. message := []byte("baz bazonk foo")
  521. hash, msg := core.SignHash(message)
  522. raw := hexutil.Bytes(message)
  523. addr, _ := mixAddr("0x694267f14675d7e1b9494fd8d72fefe1755710fa")
  524. fmt.Printf("address %v %v\n", addr.String(), addr.Original())
  525. resp, err := r.ApproveSignData(&core.SignDataRequest{
  526. Address: *addr,
  527. Message: msg,
  528. Hash: hash,
  529. Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"},
  530. Rawdata: raw,
  531. })
  532. if err != nil {
  533. t.Fatalf("Unexpected error %v", err)
  534. }
  535. if !resp.Approved {
  536. t.Fatalf("Expected approved")
  537. }
  538. }