geth, jail: contract creation tx sending fixed
This commit is contained in:
parent
caab90e62d
commit
772467c8a7
|
@ -41,6 +41,7 @@ const (
|
||||||
MaxPeers = 25
|
MaxPeers = 25
|
||||||
MaxLightPeers = 20
|
MaxLightPeers = 20
|
||||||
MaxPendingPeers = 0
|
MaxPendingPeers = 0
|
||||||
|
DefaultGas = 180000
|
||||||
|
|
||||||
ProcessFileDescriptorLimit = uint64(2048)
|
ProcessFileDescriptorLimit = uint64(2048)
|
||||||
DatabaseCacheSize = 128 // Megabytes of memory allocated to internal caching (min 16MB / database forced)
|
DatabaseCacheSize = 128 // Megabytes of memory allocated to internal caching (min 16MB / database forced)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
pragma solidity ^0.4.9;
|
||||||
|
|
||||||
|
contract Test {
|
||||||
|
function double(int a) constant returns(int) {
|
||||||
|
return 2*a;
|
||||||
|
}
|
||||||
|
}
|
114
geth/txqueue.go
114
geth/txqueue.go
|
@ -3,8 +3,6 @@ package geth
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math/big"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
@ -231,42 +229,120 @@ func sendTxArgsFromRPCCall(req RPCCall) status.SendTxArgs {
|
||||||
return status.SendTxArgs{}
|
return status.SendTxArgs{}
|
||||||
}
|
}
|
||||||
|
|
||||||
params, ok := req.Params[0].(map[string]interface{})
|
return status.SendTxArgs{
|
||||||
|
From: req.parseFromAddress(),
|
||||||
|
To: req.parseToAddress(),
|
||||||
|
Value: req.parseValue(),
|
||||||
|
Data: req.parseData(),
|
||||||
|
Gas: req.parseGas(),
|
||||||
|
GasPrice: req.parseGasPrice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RPCCall) parseFromAddress() common.Address {
|
||||||
|
params, ok := r.Params[0].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return status.SendTxArgs{}
|
return common.HexToAddress("0x")
|
||||||
}
|
}
|
||||||
|
|
||||||
from, ok := params["from"].(string)
|
from, ok := params["from"].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
from = ""
|
from = "0x"
|
||||||
|
}
|
||||||
|
|
||||||
|
return common.HexToAddress(from)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RPCCall) parseToAddress() *common.Address {
|
||||||
|
params, ok := r.Params[0].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
to, ok := params["to"].(string)
|
to, ok := params["to"].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
to = ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
param, ok := params["value"].(string)
|
address := common.HexToAddress(to)
|
||||||
|
return &address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RPCCall) parseData() hexutil.Bytes {
|
||||||
|
params, ok := r.Params[0].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
param = "0x0"
|
return hexutil.Bytes("0x")
|
||||||
}
|
|
||||||
value, err := strconv.ParseInt(param, 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
return status.SendTxArgs{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data, ok := params["data"].(string)
|
data, ok := params["data"].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
data = ""
|
data = "0x"
|
||||||
}
|
}
|
||||||
|
|
||||||
toAddress := common.HexToAddress(to)
|
byteCode, err := hexutil.Decode(data)
|
||||||
return status.SendTxArgs{
|
if err != nil {
|
||||||
From: common.HexToAddress(from),
|
byteCode = hexutil.Bytes(data)
|
||||||
To: &toAddress,
|
|
||||||
Value: (*hexutil.Big)(big.NewInt(value)),
|
|
||||||
Data: hexutil.Bytes(data),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return byteCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RPCCall) parseValue() *hexutil.Big {
|
||||||
|
params, ok := r.Params[0].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
//return (*hexutil.Big)(big.NewInt("0x0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
inputValue, ok := params["value"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedValue, err := hexutil.DecodeBig(inputValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*hexutil.Big)(parsedValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RPCCall) parseGas() *hexutil.Big {
|
||||||
|
params, ok := r.Params[0].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
inputValue, ok := params["gas"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedValue, err := hexutil.DecodeBig(inputValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*hexutil.Big)(parsedValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RPCCall) parseGasPrice() *hexutil.Big {
|
||||||
|
params, ok := r.Params[0].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
inputValue, ok := params["gasPrice"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedValue, err := hexutil.DecodeBig(inputValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*hexutil.Big)(parsedValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseJSONArray(items string) ([]string, error) {
|
func parseJSONArray(items string) ([]string, error) {
|
||||||
|
|
|
@ -14,6 +14,113 @@ import (
|
||||||
"github.com/status-im/status-go/geth"
|
"github.com/status-im/status-go/geth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestQueuedContracts(t *testing.T) {
|
||||||
|
err := geth.PrepareTestNode()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// obtain reference to status backend
|
||||||
|
lightEthereum, err := geth.NodeManagerInstance().LightEthereumService()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed: LES service is not running: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
backend := lightEthereum.StatusBackend
|
||||||
|
|
||||||
|
// create an account
|
||||||
|
sampleAddress, _, _, err := geth.CreateAccount(newAccountPassword)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("could not create account: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
geth.Logout()
|
||||||
|
|
||||||
|
// make sure you panic if transaction complete doesn't return
|
||||||
|
completeQueuedTransaction := make(chan struct{}, 1)
|
||||||
|
geth.PanicAfter(60*time.Second, completeQueuedTransaction, "TestQueuedContracts")
|
||||||
|
|
||||||
|
// replace transaction notification handler
|
||||||
|
var txHash = common.Hash{}
|
||||||
|
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||||
|
var envelope geth.SignalEnvelope
|
||||||
|
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
|
||||||
|
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if envelope.Type == geth.EventTransactionQueued {
|
||||||
|
event := envelope.Event.(map[string]interface{})
|
||||||
|
t.Logf("transaction queued (will be completed in 5 secs): {id: %s}\n", event["id"].(string))
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
|
// the first call will fail (we are not logged in, but trying to complete tx)
|
||||||
|
if txHash, err = geth.CompleteTransaction(event["id"].(string), testAddressPassword); err != status.ErrInvalidCompleteTxSender {
|
||||||
|
t.Errorf("expected error on queued transation[%v] not thrown: expected %v, got %v", event["id"], status.ErrInvalidCompleteTxSender, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// the second call will also fail (we are logged in as different user)
|
||||||
|
if err := geth.SelectAccount(sampleAddress, newAccountPassword); err != nil {
|
||||||
|
t.Errorf("cannot select account: %v", sampleAddress)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if txHash, err = geth.CompleteTransaction(event["id"].(string), testAddressPassword); err != status.ErrInvalidCompleteTxSender {
|
||||||
|
t.Errorf("expected error on queued transation[%v] not thrown: expected %v, got %v", event["id"], status.ErrInvalidCompleteTxSender, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// the third call will work as expected (as we are logged in with correct credentials)
|
||||||
|
if err := geth.SelectAccount(testAddress, testAddressPassword); err != nil {
|
||||||
|
t.Errorf("cannot select account: %v", testAddress)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if txHash, err = geth.CompleteTransaction(event["id"].(string), testAddressPassword); err != nil {
|
||||||
|
t.Errorf("cannot complete queued transation[%v]: %v", event["id"], err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("contract transaction complete: https://testnet.etherscan.io/tx/%s", txHash.Hex())
|
||||||
|
completeQueuedTransaction <- struct{}{} // so that timeout is aborted
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// this call blocks, up until Complete Transaction is called
|
||||||
|
byteCode, err := hexutil.Decode(`0x6060604052341561000c57fe5b5b60a58061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636ffa1caa14603a575bfe5b3415604157fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b60008160020290505b9190505600a165627a7a72305820ccdadd737e4ac7039963b54cee5e5afb25fa859a275252bdcf06f653155228210029`)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{
|
||||||
|
From: geth.FromAddress(testAddress),
|
||||||
|
To: nil, // marker, contract creation is expected
|
||||||
|
//Value: (*hexutil.Big)(new(big.Int).Mul(big.NewInt(1), common.Ether)),
|
||||||
|
Gas: (*hexutil.Big)(big.NewInt(geth.DefaultGas)),
|
||||||
|
Data: byteCode,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test failed: cannot send transaction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(txHash, txHashCheck) {
|
||||||
|
t.Errorf("Transaction hash returned from SendTransaction is invalid: expected %s, got %s", txHashCheck, txHash)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
|
||||||
|
if reflect.DeepEqual(txHashCheck, common.Hash{}) {
|
||||||
|
t.Error("Test failed: transaction was never queued or completed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if backend.TransactionQueue().Count() != 0 {
|
||||||
|
t.Error("tx queue must be empty at this point")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestQueuedTransactions(t *testing.T) {
|
func TestQueuedTransactions(t *testing.T) {
|
||||||
err := geth.PrepareTestNode()
|
err := geth.PrepareTestNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package jail_test
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -562,3 +563,94 @@ func TestLocalStorageSet(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContractDeployment(t *testing.T) {
|
||||||
|
err := geth.PrepareTestNode()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
jailInstance := jail.Init("")
|
||||||
|
jailInstance.Parse(CHAT_ID_CALL, "")
|
||||||
|
|
||||||
|
// obtain VM for a given chat (to send custom JS to jailed version of Send())
|
||||||
|
vm, err := jailInstance.GetVM(CHAT_ID_CALL)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("cannot get VM: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure you panic if transaction complete doesn't return
|
||||||
|
completeQueuedTransaction := make(chan struct{}, 1)
|
||||||
|
geth.PanicAfter(30*time.Second, completeQueuedTransaction, "TestContractDeployment")
|
||||||
|
|
||||||
|
// replace transaction notification handler
|
||||||
|
var txHash common.Hash
|
||||||
|
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
||||||
|
var envelope geth.SignalEnvelope
|
||||||
|
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
|
||||||
|
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if envelope.Type == geth.EventTransactionQueued {
|
||||||
|
event := envelope.Event.(map[string]interface{})
|
||||||
|
|
||||||
|
t.Logf("Transaction queued (will be completed in 5 secs): {id: %s}\n", event["id"].(string))
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
|
if err := geth.SelectAccount(TEST_ADDRESS, TEST_ADDRESS_PASSWORD); err != nil {
|
||||||
|
t.Errorf("cannot select account: %v", TEST_ADDRESS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if txHash, err = geth.CompleteTransaction(event["id"].(string), TEST_ADDRESS_PASSWORD); err != nil {
|
||||||
|
t.Errorf("cannot complete queued transation[%v]: %v", event["id"], err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
t.Logf("Contract created: https://testnet.etherscan.io/tx/%s", txHash.Hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
close(completeQueuedTransaction) // so that timeout is aborted
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err = vm.Run(`
|
||||||
|
var responseValue = null;
|
||||||
|
var testContract = web3.eth.contract([{"constant":true,"inputs":[{"name":"a","type":"int256"}],"name":"double","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"}]);
|
||||||
|
var test = testContract.new(
|
||||||
|
{
|
||||||
|
from: '` + TEST_ADDRESS + `',
|
||||||
|
data: '0x6060604052341561000c57fe5b5b60a58061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636ffa1caa14603a575bfe5b3415604157fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b60008160020290505b9190505600a165627a7a72305820ccdadd737e4ac7039963b54cee5e5afb25fa859a275252bdcf06f653155228210029',
|
||||||
|
gas: '` + strconv.Itoa(geth.DefaultGas) + `'
|
||||||
|
}, function (e, contract){
|
||||||
|
if (!e) {
|
||||||
|
responseValue = contract.transactionHash
|
||||||
|
}
|
||||||
|
})
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("cannot run custom code on VM: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
<-completeQueuedTransaction
|
||||||
|
|
||||||
|
responseValue, err := vm.Get("responseValue")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("cannot obtain result of isConnected(): %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := responseValue.ToString()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("cannot parse result: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedResponse := txHash.Hex()
|
||||||
|
if !reflect.DeepEqual(response, expectedResponse) {
|
||||||
|
t.Errorf("expected response is not returned: expected %s, got %s", expectedResponse, response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue