diff options
-rw-r--r-- | ethereum.go | 4 | ||||
-rw-r--r-- | etherpc/packages.go | 215 | ||||
-rw-r--r-- | etherpc/server.go | 62 | ||||
-rw-r--r-- | ethpub/pub.go | 21 | ||||
-rw-r--r-- | ethpub/types.go | 18 |
5 files changed, 304 insertions, 16 deletions
diff --git a/ethereum.go b/ethereum.go index b9174eaf8..d6154e213 100644 --- a/ethereum.go +++ b/ethereum.go @@ -4,6 +4,7 @@ import ( "container/list" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/etherpc" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" "io/ioutil" @@ -62,6 +63,8 @@ type Ethereum struct { MaxPeers int reactor *ethutil.ReactorEngine + + RpcServer *etherpc.JsonRpcServer } func New(caps Caps, usePnp bool) (*Ethereum, error) { @@ -336,6 +339,7 @@ func (s *Ethereum) Stop() { close(s.quit) + s.RpcServer.Stop() s.txPool.Stop() s.stateManager.Stop() diff --git a/etherpc/packages.go b/etherpc/packages.go new file mode 100644 index 000000000..5074b0219 --- /dev/null +++ b/etherpc/packages.go @@ -0,0 +1,215 @@ +package etherpc + +import ( + "encoding/json" + "errors" + "github.com/ethereum/eth-go/ethpub" + _ "log" +) + +type EthereumApi struct { + ethp *ethpub.PEthereum +} + +type JsonArgs interface { + requirements() error +} + +type BlockResponse struct { + JsonResponse +} +type GetBlockArgs struct { + BlockNumber int + Hash string +} + +type ErrorResponse struct { + Error bool `json:"error"` + ErrorText string `json:"errorText"` +} + +type JsonResponse interface { +} + +type SuccessRes struct { + Error bool `json:"error"` + Result JsonResponse `json:"result"` +} + +func NewSuccessRes(object JsonResponse) string { + e := SuccessRes{Error: false, Result: object} + res, err := json.Marshal(e) + if err != nil { + // This should never happen + panic("Creating json error response failed, help") + } + success := string(res) + return success +} + +func NewErrorResponse(msg string) error { + e := ErrorResponse{Error: true, ErrorText: msg} + res, err := json.Marshal(e) + if err != nil { + // This should never happen + panic("Creating json error response failed, help") + } + newErr := errors.New(string(res)) + return newErr +} + +func (b *GetBlockArgs) requirements() error { + if b.BlockNumber == 0 && b.Hash == "" { + return NewErrorResponse("GetBlock requires either a block 'number' or a block 'hash' as argument") + } + return nil +} + +func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *string) error { + err := args.requirements() + if err != nil { + return err + } + // Do something + block := p.ethp.GetBlock(args.Hash) + *reply = NewSuccessRes(block) + return nil +} + +type NewTxArgs struct { + Sec string + Recipient string + Value string + Gas string + GasPrice string + Init string + Body string +} +type TxResponse struct { + Hash string +} + +func (a *NewTxArgs) requirements() error { + if a.Recipient == "" { + return NewErrorResponse("Transact requires a 'recipient' address as argument") + } + if a.Value == "" { + return NewErrorResponse("Transact requires a 'value' as argument") + } + if a.Gas == "" { + return NewErrorResponse("Transact requires a 'gas' value as argument") + } + if a.GasPrice == "" { + return NewErrorResponse("Transact requires a 'gasprice' value as argument") + } + return nil +} + +func (a *NewTxArgs) requirementsContract() error { + if a.Value == "" { + return NewErrorResponse("Create requires a 'value' as argument") + } + if a.Gas == "" { + return NewErrorResponse("Create requires a 'gas' value as argument") + } + if a.GasPrice == "" { + return NewErrorResponse("Create requires a 'gasprice' value as argument") + } + if a.Body == "" { + return NewErrorResponse("Create requires a 'body' value as argument") + } + return nil +} + +func (p *EthereumApi) Transact(args *NewTxArgs, reply *string) error { + err := args.requirements() + if err != nil { + return err + } + result, _ := p.ethp.Transact(p.ethp.GetKey().PrivateKey, args.Recipient, args.Value, args.Gas, args.GasPrice, args.Body) + *reply = NewSuccessRes(result) + return nil +} + +func (p *EthereumApi) Create(args *NewTxArgs, reply *string) error { + err := args.requirementsContract() + if err != nil { + return err + } + result, _ := p.ethp.Create(p.ethp.GetKey().PrivateKey, args.Value, args.Gas, args.GasPrice, args.Init, args.Body) + *reply = NewSuccessRes(result) + return nil +} + +func (p *EthereumApi) GetKey(args interface{}, reply *string) error { + *reply = NewSuccessRes(p.ethp.GetKey()) + return nil +} + +type GetStorageArgs struct { + Address string + Key string +} + +func (a *GetStorageArgs) requirements() error { + if a.Address == "" { + return NewErrorResponse("GetStorageAt requires an 'address' value as argument") + } + if a.Key == "" { + return NewErrorResponse("GetStorageAt requires an 'key' value as argument") + } + return nil +} + +type GetStorageAtRes struct { + Key string `json:"key"` + Value string `json:"value"` + Address string `json:"address"` +} + +func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *string) error { + err := args.requirements() + if err != nil { + return err + } + state := p.ethp.GetStateObject(args.Address) + value := state.GetStorage(args.Key) + *reply = NewSuccessRes(GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value}) + return nil +} + +type GetBalanceArgs struct { + Address string +} + +func (a *GetBalanceArgs) requirements() error { + if a.Address == "" { + return NewErrorResponse("GetBalanceAt requires an 'address' value as argument") + } + return nil +} + +type BalanceRes struct { + Balance string `json:"balance"` + Address string `json:"address"` +} + +func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *string) error { + err := args.requirements() + if err != nil { + return err + } + state := p.ethp.GetStateObject(args.Address) + *reply = NewSuccessRes(BalanceRes{Balance: state.Value(), Address: args.Address}) + return nil +} + +type TestRes struct { + JsonResponse `json:"-"` + Answer int `json:"answer"` +} + +func (p *EthereumApi) Test(args *GetBlockArgs, reply *string) error { + *reply = NewSuccessRes(TestRes{Answer: 15}) + return nil +} diff --git a/etherpc/server.go b/etherpc/server.go new file mode 100644 index 000000000..0eb229536 --- /dev/null +++ b/etherpc/server.go @@ -0,0 +1,62 @@ +package etherpc + +import ( + "github.com/ethereum/eth-go/ethpub" + "github.com/ethereum/eth-go/ethutil" + "net" + "net/rpc" + "net/rpc/jsonrpc" +) + +type JsonRpcServer struct { + quit chan bool + listener net.Listener + ethp *ethpub.PEthereum +} + +func (s *JsonRpcServer) exitHandler() { +out: + for { + select { + case <-s.quit: + s.listener.Close() + break out + } + } + + ethutil.Config.Log.Infoln("[JSON] Shutdown JSON-RPC server") +} + +func (s *JsonRpcServer) Stop() { + close(s.quit) +} + +func (s *JsonRpcServer) Start() { + ethutil.Config.Log.Infoln("[JSON] Starting JSON-RPC server") + go s.exitHandler() + rpc.Register(&EthereumApi{ethp: s.ethp}) + rpc.HandleHTTP() + + for { + conn, err := s.listener.Accept() + if err != nil { + ethutil.Config.Log.Infoln("[JSON] Error starting JSON-RPC:", err) + break + } + ethutil.Config.Log.Debugln("[JSON] Incoming request.") + go jsonrpc.ServeConn(conn) + } +} + +func NewJsonRpcServer(ethp *ethpub.PEthereum) *JsonRpcServer { + l, err := net.Listen("tcp", ":30304") + if err != nil { + ethutil.Config.Log.Infoln("Error starting JSON-RPC") + } + + return &JsonRpcServer{ + listener: l, + quit: make(chan bool), + ethp: ethp, + } +} diff --git a/ethpub/pub.go b/ethpub/pub.go index f9d2ebc72..431f173a0 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -1,7 +1,6 @@ package ethpub import ( - "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethutil" ) @@ -12,11 +11,11 @@ type PEthereum struct { txPool *ethchain.TxPool } -func NewPEthereum(eth *eth.Ethereum) *PEthereum { +func NewPEthereum(sm *ethchain.StateManager, bc *ethchain.BlockChain, txp *ethchain.TxPool) *PEthereum { return &PEthereum{ - eth.StateManager(), - eth.BlockChain(), - eth.TxPool(), + sm, + bc, + txp, } } @@ -25,7 +24,15 @@ func (lib *PEthereum) GetBlock(hexHash string) *PBlock { block := lib.blockChain.GetBlock(hash) - return &PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())} + var blockInfo *PBlock + + if block != nil { + blockInfo = &PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())} + } else { + blockInfo = &PBlock{Number: -1, Hash: ""} + } + + return blockInfo } func (lib *PEthereum) GetKey() *PKey { @@ -108,7 +115,7 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, in tx = ethchain.NewContractCreationTx(value, gas, gasPrice, mainScript, initScript) } else { // Just in case it was submitted as a 0x prefixed string - if initStr[0:2] == "0x" { + if len(initStr) > 0 && initStr[0:2] == "0x" { initStr = initStr[2:len(initStr)] } tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, ethutil.FromHex(initStr)) diff --git a/ethpub/types.go b/ethpub/types.go index 7ae476339..7f25e48a6 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -8,8 +8,8 @@ import ( // Block interface exposed to QML type PBlock struct { - Number int - Hash string + Number int `json:"number"` + Hash string `json:"hash"` } // Creates a new QML Block from a chain block @@ -34,9 +34,9 @@ func NewPTx(tx *ethchain.Transaction) *PTx { } type PKey struct { - Address string - PrivateKey string - PublicKey string + Address string `json:"address"` + PrivateKey string `json:"privateKey"` + PublicKey string `json:"publicKey"` } func NewPKey(key *ethchain.KeyPair) *PKey { @@ -44,10 +44,10 @@ func NewPKey(key *ethchain.KeyPair) *PKey { } type PReceipt struct { - CreatedContract bool - Address string - Hash string - Sender string + CreatedContract bool `json:"createdContract"` + Address string `json:"address"` + Hash string `json:"hash"` + Sender string `json:"sender"` } func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt { |