diff --git a/Gopkg.lock b/Gopkg.lock index 99eff5eeb676cebb4462eedf0560de196c54c395..ce5d6fd3a2f7af0664a2a254d121a359548e2668 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -187,6 +187,12 @@ revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" +[[projects]] + name = "github.com/powerman/rpc-codec" + packages = ["jsonrpc2"] + revision = "3e1ab3b635b7b0d5f771028cd45aa9a827fd9f31" + version = "v1.0.0" + [[projects]] branch = "master" name = "github.com/rcrowley/go-metrics" @@ -354,6 +360,12 @@ revision = "9831f2c3ac1068a78f50999a30db84270f647af6" version = "v1.1" +[[projects]] + name = "github.com/ybbus/jsonrpc" + packages = ["."] + revision = "dd866631e904a5df2067d934985c5def68f391ac" + version = "v2.1.2" + [[projects]] branch = "master" name = "golang.org/x/crypto" @@ -474,6 +486,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "5db3ac43679cafd5f68104e93a248b62266d313aef2d8ba3bd7f3986a5a99834" + inputs-digest = "658b10b4a49260c9ee26dcf77b0dcbe9e39d5c1604e234c69e9db99c01ba2d0d" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index b1f9779a1f7fc121303e95d73ea11a438bde1664..e07ee9d5afb6e9cf54c9073f48f2fd167f527dd9 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -98,3 +98,11 @@ [[constraint]] name = "gopkg.in/tylerb/graceful.v1" version = "1.2.15" + +[[constraint]] + name = "github.com/powerman/rpc-codec" + version = "1.0.0" + +[[constraint]] + name = "github.com/ybbus/jsonrpc" + version = "2.1.2" diff --git a/config/config.go b/config/config.go index 160f240632db14485053c0f20a91a77f3678e97e..fa39dba4e8dffc5c6b6211fd7eb1c4c9d27377ae 100644 --- a/config/config.go +++ b/config/config.go @@ -61,15 +61,14 @@ func (conf *BurrowConfig) Kernel(ctx context.Context) (*core.Kernel, error) { var exeOptions []execution.ExecutionOption if conf.Execution != nil { - var err error exeOptions, err = conf.Execution.ExecutionOptions() if err != nil { return nil, err } } - return core.NewKernel(ctx, privValidator, conf.GenesisDoc, conf.Tendermint.TendermintConfig(), conf.RPC, exeOptions, - logger) + return core.NewKernel(ctx, keyClient, privValidator, conf.GenesisDoc, conf.Tendermint.TendermintConfig(), conf.RPC, + exeOptions, logger) } func (conf *BurrowConfig) JSONString() string { diff --git a/core/integration/test_wrapper.go b/core/integration/test_wrapper.go index b532e4e1021b1171e5ae9f156468bb569a3bac79..c5ee2892a651dcfac896cd7cc62a5731955848f3 100644 --- a/core/integration/test_wrapper.go +++ b/core/integration/test_wrapper.go @@ -46,7 +46,7 @@ const ( var debugLogging = false // We use this to wrap tests -func TestWrapper(privateAccounts []acm.PrivateAccount, genesisDoc *genesis.GenesisDoc, runner func() int) int { +func TestWrapper(privateAccounts []acm.PrivateAccount, genesisDoc *genesis.GenesisDoc, runner func(*core.Kernel) int) int { fmt.Println("Running with integration TestWrapper (core/integration/test_wrapper.go)...") os.RemoveAll(testDir) @@ -97,7 +97,7 @@ func TestWrapper(privateAccounts []acm.PrivateAccount, genesisDoc *genesis.Genes panic(err) } - return runner() + return runner(kernel) } func TestGenesisDoc(addressables []acm.PrivateAccount) *genesis.GenesisDoc { diff --git a/core/kernel_test.go b/core/kernel_test.go index aa1af7efc1aefc498cef965014a6d163d6e7f5ab..e1e4d65d17c0416ae3ea0a613e35632c059915e6 100644 --- a/core/kernel_test.go +++ b/core/kernel_test.go @@ -12,6 +12,7 @@ import ( "github.com/hyperledger/burrow/consensus/tendermint" "github.com/hyperledger/burrow/consensus/tendermint/validator" "github.com/hyperledger/burrow/genesis" + "github.com/hyperledger/burrow/keys/mock" "github.com/hyperledger/burrow/logging" "github.com/hyperledger/burrow/rpc" "github.com/stretchr/testify/assert" @@ -64,7 +65,8 @@ func bootWaitBlocksShutdown(privValidator tm_types.PrivValidator, genesisDoc *ge tmConf *tm_config.Config, logger *logging.Logger, blockChecker func(block *tm_types.EventDataNewBlock) (cont bool)) error { - kern, err := NewKernel(context.Background(), privValidator, genesisDoc, tmConf, rpc.DefaultRPCConfig(), nil, logger) + kern, err := NewKernel(context.Background(), mock.NewMockKeyClient(), privValidator, genesisDoc, tmConf, + rpc.DefaultRPCConfig(), nil, logger) if err != nil { return err } diff --git a/execution/transactor.go2 b/execution/transactor.go2 new file mode 100644 index 0000000000000000000000000000000000000000..9cdd63a23eec1368d457e0a90012e3e306064b03 --- /dev/null +++ b/execution/transactor.go2 @@ -0,0 +1,377 @@ +// Copyright 2017 Monax Industries Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package execution + +import ( + "context" + "fmt" + "runtime/debug" + "sync" + "time" + + acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/account/state" + "github.com/hyperledger/burrow/binary" + "github.com/hyperledger/burrow/blockchain" + "github.com/hyperledger/burrow/consensus/tendermint/codes" + "github.com/hyperledger/burrow/event" + exe_events "github.com/hyperledger/burrow/execution/events" + "github.com/hyperledger/burrow/execution/evm" + evm_events "github.com/hyperledger/burrow/execution/evm/events" + "github.com/hyperledger/burrow/logging" + "github.com/hyperledger/burrow/logging/structure" + "github.com/hyperledger/burrow/txs" + abci_types "github.com/tendermint/abci/types" + "github.com/tendermint/go-wire" +) + +const BlockingTimeoutSeconds = 30 + +type Call struct { + Return []byte + GasUsed uint64 +} + +type SequencedAddressableSigner interface { + acm.AddressableSigner + Sequence() uint64 +} + +// Transactor is the controller/middleware for the v0 RPC +type Transactor struct { + sync.Mutex + tip blockchain.Tip + eventEmitter event.Emitter + broadcastTxAsync func(tx txs.Tx, callback func(res *abci_types.Response)) error + logger *logging.Logger +} + +func NewTransactor(tip blockchain.Tip, eventEmitter event.Emitter, + broadcastTxAsync func(tx txs.Tx, callback func(res *abci_types.Response)) error, + logger *logging.Logger) *Transactor { + + return &Transactor{ + tip: tip, + eventEmitter: eventEmitter, + broadcastTxAsync: broadcastTxAsync, + logger: logger.With(structure.ComponentKey, "Transactor"), + } +} + +// Run a contract's code on an isolated and unpersisted state +// Cannot be used to create new contracts +func (trans *Transactor) Call(iterable state.Iterable, fromAddress, toAddress acm.Address, + data []byte) (call *Call, err error) { + + if evm.RegisteredNativeContract(toAddress.Word256()) { + return nil, fmt.Errorf("attempt to call native contract at address "+ + "%X, but native contracts can not be called directly. Use a deployed "+ + "contract that calls the native function instead", toAddress) + } + // This was being run against CheckTx cache, need to understand the reasoning + callee, err := state.GetMutableAccount(iterable, toAddress) + if err != nil { + return nil, err + } + if callee == nil { + return nil, fmt.Errorf("account %s does not exist", toAddress) + } + caller := acm.ConcreteAccount{Address: fromAddress}.MutableAccount() + txCache := state.NewCache(iterable) + params := vmParams(trans.tip) + + vmach := evm.NewVM(txCache, params, caller.Address(), nil, trans.logger.WithScope("Call")) + vmach.SetPublisher(trans.eventEmitter) + + gas := params.GasLimit + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic from VM in simulated call: %v\n%s", r, debug.Stack()) + } + }() + ret, err := vmach.Call(caller, callee, callee.Code(), data, 0, &gas) + if err != nil { + return nil, err + } + gasUsed := params.GasLimit - gas + return &Call{Return: ret, GasUsed: gasUsed}, nil +} + +// Run the given code on an isolated and unpersisted state +// Cannot be used to create new contracts. +func (trans *Transactor) CallCode(iterable state.Iterable, fromAddress acm.Address, code, data []byte) (*Call, error) { + // This was being run against CheckTx cache, need to understand the reasoning + callee := acm.ConcreteAccount{Address: fromAddress}.MutableAccount() + caller := acm.ConcreteAccount{Address: fromAddress}.MutableAccount() + txCache := state.NewCache(iterable) + params := vmParams(trans.tip) + + vmach := evm.NewVM(txCache, params, caller.Address(), nil, trans.logger.WithScope("CallCode")) + gas := params.GasLimit + ret, err := vmach.Call(caller, callee, code, data, 0, &gas) + if err != nil { + return nil, err + } + gasUsed := params.GasLimit - gas + return &Call{Return: ret, GasUsed: gasUsed}, nil +} + +func (trans *Transactor) BroadcastTxAsync(tx txs.Tx, callback func(res *abci_types.Response)) error { + return trans.broadcastTxAsync(tx, callback) +} + +// Broadcast a transaction. +func (trans *Transactor) BroadcastTx(tx txs.Tx) (*txs.Receipt, error) { + trans.logger.Trace.Log("method", "BroadcastTx", + "tx_hash", tx.Hash(trans.tip.ChainID()), + "tx", tx.String()) + responseCh := make(chan *abci_types.Response, 1) + err := trans.BroadcastTxAsync(tx, func(res *abci_types.Response) { + responseCh <- res + }) + + if err != nil { + return nil, err + } + response := <-responseCh + checkTxResponse := response.GetCheckTx() + if checkTxResponse == nil { + return nil, fmt.Errorf("application did not return CheckTx response") + } + + switch checkTxResponse.Code { + case codes.TxExecutionSuccessCode: + receipt := new(txs.Receipt) + err := wire.ReadBinaryBytes(checkTxResponse.Data, receipt) + if err != nil { + return nil, fmt.Errorf("could not deserialise transaction receipt: %s", err) + } + return receipt, nil + default: + return nil, fmt.Errorf("error returned by Tendermint in BroadcastTxSync "+ + "ABCI code: %v, ABCI log: %v", checkTxResponse.Code, checkTxResponse.Log) + } +} + +// Orders calls to BroadcastTx using lock (waits for response from core before releasing) +func (trans *Transactor) Transact(inputAccount SequencedAddressableSigner, address *acm.Address, data []byte, gasLimit, + fee uint64) (*txs.Receipt, error) { + trans.Lock() + defer trans.Unlock() + // TODO: [Silas] we should consider revising this method and removing fee, or + // possibly adding an amount parameter. It is non-sensical to just be able to + // set the fee. Our support of fees in general is questionable since at the + // moment all we do is deduct the fee effectively leaking token. It is possible + // someone may be using the sending of native token to payable functions but + // they can be served by broadcasting a token. + + // We hard-code the amount to be equal to the fee which means the CallTx we + // generate transfers 0 value, which is the most sensible default since in + // recent solidity compilers the EVM generated will throw an error if value + // is transferred to a non-payable function. + txInput := &txs.TxInput{ + Address: inputAccount.Address(), + Amount: fee, + Sequence: inputAccount.Sequence() + 1, + PublicKey: inputAccount.PublicKey(), + } + tx := &txs.CallTx{ + Input: txInput, + Address: address, + GasLimit: gasLimit, + Fee: fee, + Data: data, + } + + // Got ourselves a tx. + err := tx.Sign(trans.tip.ChainID(), inputAccount) + if err != nil { + return nil, err + } + return trans.BroadcastTx(tx) +} + +func (trans *Transactor) TransactAndHold(inputAccount SequencedAddressableSigner, privKey []byte, + address *acm.Address, data []byte, gasLimit, fee uint64) (*evm_events.EventDataCall, error) { + + receipt, err := trans.Transact(inputAccount, address, data, gasLimit, fee) + if err != nil { + return nil, err + } + + // We want non-blocking on the first event received (but buffer the value), + // after which we want to block (and then discard the value - see below) + wc := make(chan *evm_events.EventDataCall, 1) + + subID, err := event.GenerateSubscriptionID() + if err != nil { + return nil, err + } + + err = evm_events.SubscribeAccountCall(context.Background(), trans.eventEmitter, subID, receipt.ContractAddress, + receipt.TxHash, wc) + if err != nil { + return nil, err + } + // Will clean up callback goroutine and subscription in pubsub + defer trans.eventEmitter.UnsubscribeAll(context.Background(), subID) + + timer := time.NewTimer(BlockingTimeoutSeconds * time.Second) + defer timer.Stop() + + select { + case <-timer.C: + return nil, fmt.Errorf("transaction timed out TxHash: %X", receipt.TxHash) + case eventDataCall := <-wc: + if eventDataCall.Exception != "" { + return nil, fmt.Errorf("error when transacting: " + eventDataCall.Exception) + } else { + return eventDataCall, nil + } + } +} + +func (trans *Transactor) Send(inputAccount SequencedAddressableSigner, toAddress acm.Address, amount uint64) (*txs.Receipt, error) { + if len(privKey) != 64 { + return nil, fmt.Errorf("Private key is not of the right length: %d\n", + len(privKey)) + } + + pk := &[64]byte{} + copy(pk[:], privKey) + trans.Lock() + defer trans.Unlock() + pa, err := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) + if err != nil { + return nil, err + } + acc, err := trans.state.GetAccount(pa.Address()) + if err != nil { + return nil, err + } + sequence := uint64(1) + if acc != nil { + sequence = acc.Sequence() + uint64(1) + } + + tx := txs.NewSendTx() + + txInput := &txs.TxInput{ + Address: pa.Address(), + Amount: amount, + Sequence: sequence, + PublicKey: pa.PublicKey(), + } + + tx.Inputs = append(tx.Inputs, txInput) + + txOutput := &txs.TxOutput{Address: toAddress, Amount: amount} + + tx.Outputs = append(tx.Outputs, txOutput) + + // Got ourselves a tx. + txS, errS := trans.SignTx(tx, []acm.AddressableSigner{pa}) + if errS != nil { + return nil, errS + } + return trans.BroadcastTx(txS) +} + +func (trans *Transactor) SendAndHold(privKey []byte, toAddress acm.Address, amount uint64) (*txs.Receipt, error) { + receipt, err := trans.Send(privKey, toAddress, amount) + if err != nil { + return nil, err + } + + wc := make(chan *txs.SendTx) + + subID, err := event.GenerateSubscriptionID() + if err != nil { + return nil, err + } + + err = exe_events.SubscribeAccountOutputSendTx(context.Background(), trans.eventEmitter, subID, toAddress, + receipt.TxHash, wc) + if err != nil { + return nil, err + } + defer trans.eventEmitter.UnsubscribeAll(context.Background(), subID) + + timer := time.NewTimer(BlockingTimeoutSeconds * time.Second) + defer timer.Stop() + + pa, err := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) + if err != nil { + return nil, err + } + + select { + case <-timer.C: + return nil, fmt.Errorf("transaction timed out TxHash: %X", receipt.TxHash) + case sendTx := <-wc: + // This is a double check - we subscribed to this tx's hash so something has gone wrong if the amounts don't match + if sendTx.Inputs[0].Address == pa.Address() && sendTx.Inputs[0].Amount == amount { + return receipt, nil + } + return nil, fmt.Errorf("received SendTx but hash doesn't seem to match what we subscribed to, "+ + "received SendTx: %v which does not match receipt on sending: %v", sendTx, receipt) + } +} + +func (trans *Transactor) TransactNameReg(privKey []byte, name, data string, amount, fee uint64) (*txs.Receipt, error) { + if len(privKey) != 64 { + return nil, fmt.Errorf("Private key is not of the right length: %d\n", len(privKey)) + } + trans.Lock() + defer trans.Unlock() + pa, err := acm.GeneratePrivateAccountFromPrivateKeyBytes(privKey) + if err != nil { + return nil, err + } + acc, err := trans.state.GetAccount(pa.Address()) + if err != nil { + return nil, err + } + sequence := uint64(1) + if acc == nil { + sequence = acc.Sequence() + uint64(1) + } + tx := txs.NewNameTxWithSequence(pa.PublicKey(), name, data, amount, fee, sequence) + // Got ourselves a tx. + txS, errS := trans.SignTx(tx, []acm.AddressableSigner{pa}) + if errS != nil { + return nil, errS + } + return trans.BroadcastTx(txS) +} + +// Sign a transaction +func (trans *Transactor) SignTx(tx txs.Tx, signingAccounts []acm.AddressableSigner) (txs.Tx, error) { + // more checks? + err := tx.Sign(trans.tip.ChainID(), signingAccounts...) + if err != nil { + return nil, err + } + return tx, nil +} + +func vmParams(tip blockchain.Tip) evm.Params { + return evm.Params{ + BlockHeight: tip.LastBlockHeight(), + BlockHash: binary.LeftPadWord256(tip.LastBlockHash()), + BlockTime: tip.LastBlockTime().Unix(), + GasLimit: GasLimit, + } +} diff --git a/execution/transactor_test.go b/execution/transactor_test.go index 83c281a425e82d011ae512ded8ecdc51dcdeb50f..7b0cdbfde99d760453eb6e7755a148bebd48b45f 100644 --- a/execution/transactor_test.go +++ b/execution/transactor_test.go @@ -40,3 +40,7 @@ func newTestTransactor(txProcessor func(tx txs.Tx) (*types.Response, error)) tes Transactor: trans, } } + +func TestTransactor_Transact(t *testing.T) { + //trans := newTestTransactor() +} diff --git a/logging/logger.go b/logging/logger.go index 51346c4f18d34ebd7c60603ee562afbf724c2509..03244b4da435bac02d64bd0687801df789f1a7e7 100644 --- a/logging/logger.go +++ b/logging/logger.go @@ -144,6 +144,6 @@ func Msg(logger kitlog.Logger, message string, keyvals ...interface{}) error { // Wrap the output loggers with a a set of standard transforms, a non-blocking // ChannelLogger and an outer context func wrapOutputLogger(outputLogger kitlog.Logger) (kitlog.Logger, channels.Channel) { - //return loggers.NonBlockingLogger(loggers.BurrowFormatLogger(outputLogger)) - return loggers.BurrowFormatLogger(outputLogger), channels.NewDeadChannel() + return loggers.NonBlockingLogger(loggers.BurrowFormatLogger(outputLogger)) + //return loggers.BurrowFormatLogger(outputLogger), channels.NewDeadChannel() } diff --git a/rpc/jsonrpc.go b/rpc/jsonrpc.go index 17b683beb81c67d4d9778ab15153fd9e36f6dfb8..b9b52e910f3070e00472b3b3b35876a638cdcb4d 100644 --- a/rpc/jsonrpc.go +++ b/rpc/jsonrpc.go @@ -16,6 +16,7 @@ package rpc import ( "encoding/json" + "fmt" ) // JSON-RPC 2.0 error codes. @@ -66,6 +67,10 @@ type ( } ) +func (err RPCError) Error() string { + return fmt.Sprintf("Error %v: %s", err.Code, err.Message) +} + // Create a new RPC request. This is the generic struct that is passed to RPC // methods func NewRPCRequest(id string, method string, params json.RawMessage) *RPCRequest { diff --git a/rpc/tm/integration/shared_test.go b/rpc/tm/integration/main_test.go similarity index 93% rename from rpc/tm/integration/shared_test.go rename to rpc/tm/integration/main_test.go index 55a2a5b0b4e99955bae50b118c1529d8ba438981..9833d45c9668313b8104da27c639286373b376d0 100644 --- a/rpc/tm/integration/shared_test.go +++ b/rpc/tm/integration/main_test.go @@ -22,12 +22,13 @@ import ( "testing" "time" + "github.com/hyperledger/burrow/core" "github.com/hyperledger/burrow/core/integration" ) // Needs to be in a _test.go file to be picked up func TestMain(m *testing.M) { - returnValue := integration.TestWrapper(privateAccounts, genesisDoc, func() int { + returnValue := integration.TestWrapper(privateAccounts, genesisDoc, func(*core.Kernel) int { return m.Run() }) diff --git a/rpc/tm/integration/shared.go b/rpc/tm/integration/shared.go index 0f4ba18a5091a7d251fe6ba49472a3d8e1fe6c90..e9c1981f3554daaf5af1d065ac41d3db7b53ae62 100644 --- a/rpc/tm/integration/shared.go +++ b/rpc/tm/integration/shared.go @@ -48,7 +48,6 @@ var ( "JSONRPC": jsonRpcClient, "HTTP": httpClient, } - // Initialised in initGlobalVariables genesisDoc = integration.TestGenesisDoc(privateAccounts) ) diff --git a/rpc/v0/client.go b/rpc/v0/client.go new file mode 100644 index 0000000000000000000000000000000000000000..791f89310977068cd3b957a9fd86ad707f915175 --- /dev/null +++ b/rpc/v0/client.go @@ -0,0 +1,70 @@ +package v0 + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "net/http" + "time" + + "github.com/hyperledger/burrow/rpc" +) + +type V0Client struct { + url string + codec rpc.Codec + client *http.Client +} + +type RPCResponse struct { + Result json.RawMessage `json:"result"` + Error *rpc.RPCError `json:"error"` + Id string `json:"id"` + JSONRPC string `json:"jsonrpc"` +} + +func NewV0Client(url string) *V0Client { + return &V0Client{ + url: url, + codec: NewTCodec(), + client: &http.Client{ + Timeout: 10 * time.Second, + }, + } +} + +func (vc *V0Client) Call(method string, param interface{}, result interface{}) error { + // Marhsal into JSONRPC request object + bs, err := vc.codec.EncodeBytes(param) + if err != nil { + return err + } + request := rpc.NewRPCRequest("test", method, bs) + bs, err = json.Marshal(request) + if err != nil { + return err + } + // Post to JSONService + resp, err := vc.client.Post(vc.url, "application/json", bytes.NewBuffer(bs)) + if err != nil { + return err + } + // Marshal into JSONRPC response object + bs, err = ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + rpcResponse := new(RPCResponse) + err = json.Unmarshal(bs, rpcResponse) + if err != nil { + return err + } + if rpcResponse.Error != nil { + return rpcResponse.Error + } + err = json.Unmarshal(rpcResponse.Result, result) + if err != nil { + return err + } + return nil +} diff --git a/rpc/v0/integration/main_test.go b/rpc/v0/integration/main_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5a0375653533e0e49b14512aca4d1517115783fc --- /dev/null +++ b/rpc/v0/integration/main_test.go @@ -0,0 +1,42 @@ +// +build integration + +// Space above here matters +// Copyright 2017 Monax Industries Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integration + +import ( + "os" + "testing" + "time" + + "github.com/hyperledger/burrow/core" + "github.com/hyperledger/burrow/core/integration" +) + +var privateAccounts = integration.MakePrivateAccounts(5) // make keys +var genesisDoc = integration.TestGenesisDoc(privateAccounts) +var kernel *core.Kernel + +// Needs to be in a _test.go file to be picked up +func TestMain(m *testing.M) { + returnValue := integration.TestWrapper(privateAccounts, genesisDoc, func(kern *core.Kernel) int { + kernel = kern + return m.Run() + }) + + time.Sleep(3 * time.Second) + os.Exit(returnValue) +} diff --git a/rpc/v0/integration/v0_test.go b/rpc/v0/integration/v0_test.go new file mode 100644 index 0000000000000000000000000000000000000000..433b2fae0178a1883b8d752b8a2f33d3dd738483 --- /dev/null +++ b/rpc/v0/integration/v0_test.go @@ -0,0 +1,45 @@ +// +build integration + +// Space above here matters +// Copyright 2017 Monax Industries Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integration + +import ( + "testing" + + "github.com/hyperledger/burrow/rpc/v0" + "github.com/hyperledger/burrow/txs" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTransact(t *testing.T) { + cli := v0.NewV0Client("http://localhost:1337/rpc") + receipt := new(txs.Receipt) + + address := privateAccounts[1].Address() + param := v0.TransactParam{ + PrivKey: privateAccounts[0].PrivateKey().RawBytes(), + Address: address.Bytes(), + Data: []byte{}, + Fee: 2, + GasLimit: 10000, + } + err := cli.Call(v0.TRANSACT, param, receipt) + require.NoError(t, err) + assert.False(t, receipt.CreatesContract) + assert.Equal(t, address, receipt.ContractAddress) +} diff --git a/rpc/v0/methods.go b/rpc/v0/methods.go index 3d5c98b72bbe8e2ecf46a693c95ebefe61acd921..cfa075871e2ef4d228399b235888b467e36e83b1 100644 --- a/rpc/v0/methods.go +++ b/rpc/v0/methods.go @@ -229,6 +229,11 @@ func GetMethods(codec rpc.Codec, service *rpc.Service, logger *logging.Logger) m if err != nil { return nil, rpc.INVALID_PARAMS, err } + //inputAccount, err := service.Mempool().SigningAccountFromPrivateKey(param.PrivKey) + //if err != nil { + // return nil, rpc.INVALID_PARAMS, err + //} + //receipt, err := service.Transactor().Transact2(inputAccount, address, param.Data, param.GasLimit, param.Fee) receipt, err := service.Transactor().Transact(param.PrivKey, address, param.Data, param.GasLimit, param.Fee) if err != nil { return nil, rpc.INTERNAL_ERROR, err