From c49a833eb9341f21983dd18ca4cc78173ad0bcfd Mon Sep 17 00:00:00 2001 From: namtruong Date: Wed, 5 Dec 2018 14:30:42 +0000 Subject: [PATCH] add support for private sendRawTransaction - merge with latest quorum --- docs/api.md | 51 ++++++++++++++++++++++++++ internal/ethapi/api.go | 39 ++++++++++++++++++-- internal/web3ext/web3ext.go | 6 +++ private/constellation/constellation.go | 12 ++++++ private/constellation/node.go | 24 ++++++++++++ private/private.go | 1 + 6 files changed, 130 insertions(+), 3 deletions(-) diff --git a/docs/api.md b/docs/api.md index d7e2b84d0..143c9a6f3 100644 --- a/docs/api.md +++ b/docs/api.md @@ -3,6 +3,8 @@ ## Privacy APIs +#### eth.sendTransaction + __To support private transactions in Quorum, the `web3.eth.sendTransaction(object)` API method has been modified.__ ```js @@ -50,6 +52,55 @@ web3.eth.sendTransaction({ ``` *** +#### eth.sendRawPrivateTransaction + +__To support sending raw transactions in Quorum, the `web3.eth.sendRawPrivateTransaction(string, object)` API method has been created.__ + +```js +web3.eth.sendRawPrivateTransaction(signedTransactionData [, privateData] [, callback]) +``` + +Sends an already signed transaction. For example can be signed using: https://github.com/SilentCicero/ethereumjs-accounts + +__Important:__ Please note that before calling this API, a `storeraw` api need to be called first to Quorum's private transaction manager. Instructions on how to do this can be found [here](https://github.com/jpmorganchase/tessera/wiki/Interface-&-API). + +##### Parameters + 1. `String` - Signed transaction data in HEX format + 2. `Object` - Private data to send + - `privateFor`: `List` - When sending a private transaction, an array of the recipients' base64-encoded public keys. +3. `Function` - (optional) If you pass a callback the HTTP request is made asynchronous. See [this note](#using-callbacks) for details. + + ##### Returns + `String` - The 32 Bytes transaction hash as HEX string. + If the transaction was a contract creation use [web3.eth.getTransactionReceipt()](#web3ethgettransactionreceipt) to get the contract address, after the transaction was mined. + + + ##### Example + ```js + var Tx = require('ethereumjs-tx'); + var privateKey = new Buffer('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', 'hex') + var rawTx = { + nonce: '0x00', + gasPrice: '0x09184e72a000', + gasLimit: '0x2710', + to: '0x0000000000000000000000000000000000000000', + value: '0x00', + // This data should be the hex value of the hash returned by Quorum's privacy transaction manager after invoking storeraw api + data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057' + } + var tx = new Tx(rawTx); + tx.sign(privateKey); + var serializedTx = tx.serialize(); + //console.log(serializedTx.toString('hex')); + //f889808609184e72a00082271094000000000000000000000000000000000000000080a47f74657374320000000000000000000000000000000000000000000000000000006000571ca08a8bbf888cfa37bbf0bb965423625641fc956967b81d12e23709cead01446075a01ce999b56a8a88504be365442ea61239198e23d1fce7d00fcfc5cd3b44b7215f + web3.eth.sendRawPrivateTransaction('0x' + serializedTx.toString('hex'), {privateFor: ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]}, function(err, hash) { + if (!err) + console.log(hash); // "0x7f9fade1c0d57a7af66ab4ead79fade1c0d57a7af66ab4ead7c2c2eb7b11a91385" + }); + ``` + + + ## JSON RPC Privacy API Reference __In addition to the JSON-RPC provided by Ethereum, Quorum exposes below two API calls.__ diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 567c33cc9..dc0718d36 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1168,12 +1168,17 @@ type SendTxArgs struct { Input *hexutil.Bytes `json:"input"` //Quorum - PrivateFrom string `json:"privateFrom"` - PrivateFor []string `json:"privateFor"` - PrivateTxType string `json:"restriction"` + PrivateFrom string `json:"privateFrom"` + PrivateFor []string `json:"privateFor"` + PrivateTxType string `json:"restriction"` //End-Quorum } +// SendRawTxArgs represents the arguments to submit a new signed private transaction into the transaction pool. +type SendRawTxArgs struct { + PrivateFor []string `json:"privateFor"` +} + // setDefaults is a helper function that fills in default values for unspecified tx fields. func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { if args.Gas == nil { @@ -1315,6 +1320,33 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod return submitTransaction(ctx, s.b, tx, tx.IsPrivate()) } +// SendRawPrivateTransaction will add the signed transaction to the transaction pool. +// The sender is responsible for signing the transaction and using the correct nonce. +func (s *PublicTransactionPoolAPI) SendRawPrivateTransaction(ctx context.Context, encodedTx hexutil.Bytes, args SendRawTxArgs) (common.Hash, error) { + + tx := new(types.Transaction) + if err := rlp.DecodeBytes(encodedTx, tx); err != nil { + return common.Hash{}, err + } + + txHash := []byte(tx.Data()) + isPrivate := args.PrivateFor != nil + + if isPrivate { + if len(txHash) > 0 { + //Send private transaction to privacy manager + log.Info("sending private tx", "data", fmt.Sprintf("%x", txHash), "privatefor", args.PrivateFor) + result, err := private.P.SendSignedTx(txHash, args.PrivateFor) + log.Info("sent private tx", "result", fmt.Sprintf("%x", result), "privatefor", args.PrivateFor) + if err != nil { + return common.Hash{}, err + } + } + } + + return submitTransaction(ctx, s.b, tx, isPrivate) +} + // Sign calculates an ECDSA signature for: // keccack256("\x19Ethereum Signed Message:\n" + len(message) + message). // @@ -1688,4 +1720,5 @@ func (s *PublicBlockChainAPI) GetQuorumPayload(digestHex string) (string, error) } return fmt.Sprintf("0x%x", data), nil } + //End-Quorum diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 24c9f5297..c4a13593e 100755 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -396,6 +396,12 @@ const Eth_JS = ` web3._extend({ property: 'eth', methods: [ + new web3._extend.Method({ + name: 'sendRawPrivateTransaction', + call: 'eth_sendRawPrivateTransaction', + params: 2, + inputFormatter: [null, null] + }), new web3._extend.Method({ name: 'sign', call: 'eth_sign', diff --git a/private/constellation/constellation.go b/private/constellation/constellation.go index 69afcde68..3cc9aa62c 100644 --- a/private/constellation/constellation.go +++ b/private/constellation/constellation.go @@ -33,6 +33,18 @@ func (g *Constellation) Send(data []byte, from string, to []string) (out []byte, return out, nil } +func (g *Constellation) SendSignedTx(data []byte, to []string) (out []byte, err error) { + if g.isConstellationNotInUse { + return nil, ErrConstellationIsntInit + } + out, err = g.node.SendSignedPayload(data, to) + if err != nil { + return nil, err + } + return out, nil +} + + func (g *Constellation) Receive(data []byte) ([]byte, error) { if g.isConstellationNotInUse { return nil, nil diff --git a/private/constellation/node.go b/private/constellation/node.go index ce4a420eb..50263aae6 100644 --- a/private/constellation/node.go +++ b/private/constellation/node.go @@ -106,6 +106,30 @@ func (c *Client) SendPayload(pl []byte, b64From string, b64To []string) ([]byte, return ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, res.Body)) } +func (c *Client) SendSignedPayload(signedPayload []byte, b64To []string) ([]byte, error) { + buf := bytes.NewBuffer(signedPayload) + req, err := http.NewRequest("POST", "http+unix://c/sendsignedtx", buf) + if err != nil { + return nil, err + } + + req.Header.Set("c11n-to", strings.Join(b64To, ",")) + req.Header.Set("Content-Type", "application/octet-stream") + res, err := c.httpClient.Do(req) + + if res != nil { + defer res.Body.Close() + } + if err != nil { + return nil, err + } + if res.StatusCode != 200 { + return nil, fmt.Errorf("Non-200 status code: %+v", res) + } + + return ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, res.Body)) +} + func (c *Client) ReceivePayload(key []byte) ([]byte, error) { req, err := http.NewRequest("GET", "http+unix://c/receiveraw", nil) if err != nil { diff --git a/private/private.go b/private/private.go index 2cf2175bf..bcbad08da 100644 --- a/private/private.go +++ b/private/private.go @@ -8,6 +8,7 @@ import ( type PrivateTransactionManager interface { Send(data []byte, from string, to []string) ([]byte, error) + SendSignedTx(data []byte, to []string) ([]byte, error) Receive(data []byte) ([]byte, error) }