mirror of https://github.com/poanetwork/quorum.git
Quorum private transaction support for Abigen (#819)
Support private transaction for abigen and update private abigen docs
This commit is contained in:
parent
e1278520d0
commit
26bab38682
|
@ -81,7 +81,9 @@ type ContractTransactor interface {
|
||||||
// for setting a reasonable default.
|
// for setting a reasonable default.
|
||||||
EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error)
|
EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error)
|
||||||
// SendTransaction injects the transaction into the pending pool for execution.
|
// SendTransaction injects the transaction into the pending pool for execution.
|
||||||
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
SendTransaction(ctx context.Context, tx *types.Transaction, args PrivateTxArgs) error
|
||||||
|
// PreparePrivateTransaction send the private transaction to Tessera/Constellation's /storeraw API using HTTP
|
||||||
|
PreparePrivateTransaction(data []byte, privateFrom string) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractFilterer defines the methods needed to access log events using one-off
|
// ContractFilterer defines the methods needed to access log events using one-off
|
||||||
|
|
|
@ -293,7 +293,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||||
|
|
||||||
// SendTransaction updates the pending block to include the given transaction.
|
// SendTransaction updates the pending block to include the given transaction.
|
||||||
// It panics if the transaction is invalid.
|
// It panics if the transaction is invalid.
|
||||||
func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction, args bind.PrivateTxArgs) error {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
@ -319,6 +319,11 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PreparePrivateTransaction dummy implementation
|
||||||
|
func (b *SimulatedBackend) PreparePrivateTransaction(data []byte, privateFrom string) ([]byte, error) {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
// FilterLogs executes a log filter operation, blocking during execution and
|
// FilterLogs executes a log filter operation, blocking during execution and
|
||||||
// returning all the results in one batch.
|
// returning all the results in one batch.
|
||||||
//
|
//
|
||||||
|
|
|
@ -34,6 +34,13 @@ import (
|
||||||
// sign the transaction before submission.
|
// sign the transaction before submission.
|
||||||
type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error)
|
type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error)
|
||||||
|
|
||||||
|
// Quorum
|
||||||
|
//
|
||||||
|
// Additional arguments in order to support transaction privacy
|
||||||
|
type PrivateTxArgs struct {
|
||||||
|
PrivateFor []string `json:"privateFor"`
|
||||||
|
}
|
||||||
|
|
||||||
// CallOpts is the collection of options to fine tune a contract call request.
|
// CallOpts is the collection of options to fine tune a contract call request.
|
||||||
type CallOpts struct {
|
type CallOpts struct {
|
||||||
Pending bool // Whether to operate on the pending state or the last known one
|
Pending bool // Whether to operate on the pending state or the last known one
|
||||||
|
@ -54,6 +61,10 @@ type TransactOpts struct {
|
||||||
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
|
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
|
||||||
|
|
||||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||||
|
|
||||||
|
// Quorum
|
||||||
|
PrivateFrom string // The public key of the Tessera/Constellation identity to send this tx from.
|
||||||
|
PrivateFor []string // The public keys of the Tessera/Constellation identities this tx is intended for.
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterOpts is the collection of options to fine tune filtering for events
|
// FilterOpts is the collection of options to fine tune filtering for events
|
||||||
|
@ -231,16 +242,36 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
||||||
} else {
|
} else {
|
||||||
rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input)
|
rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input)
|
||||||
}
|
}
|
||||||
if opts.Signer == nil {
|
|
||||||
return nil, errors.New("no signer to authorize the transaction with")
|
// If this transaction is private, we need to substitute the data payload
|
||||||
}
|
// with the hash of the transaction from tessera/constellation.
|
||||||
signedTx, err := opts.Signer(types.HomesteadSigner{}, opts.From, rawTx)
|
if opts.PrivateFor != nil {
|
||||||
|
var payload []byte
|
||||||
|
payload, err = c.transactor.PreparePrivateTransaction(rawTx.Data(), opts.PrivateFrom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
|
rawTx = c.createPrivateTransaction(rawTx, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choose signer to sign transaction
|
||||||
|
if opts.Signer == nil {
|
||||||
|
return nil, errors.New("no signer to authorize the transaction with")
|
||||||
|
}
|
||||||
|
var signedTx *types.Transaction
|
||||||
|
if rawTx.IsPrivate() {
|
||||||
|
signedTx, err = opts.Signer(types.QuorumPrivateTxSigner{}, opts.From, rawTx)
|
||||||
|
} else {
|
||||||
|
signedTx, err = opts.Signer(types.HomesteadSigner{}, opts.From, rawTx)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx, PrivateTxArgs{PrivateFor: opts.PrivateFor}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return signedTx, nil
|
return signedTx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,6 +371,18 @@ func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log)
|
||||||
return parseTopics(out, indexed, log.Topics[1:])
|
return parseTopics(out, indexed, log.Topics[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createPrivateTransaction replaces the payload of private transaction to the hash from Tessera/Constellation
|
||||||
|
func (c *BoundContract) createPrivateTransaction(tx *types.Transaction, payload []byte) *types.Transaction {
|
||||||
|
var privateTx *types.Transaction
|
||||||
|
if tx.To() == nil {
|
||||||
|
privateTx = types.NewContractCreation(tx.Nonce(), tx.Value(), tx.Gas(), tx.GasPrice(), payload)
|
||||||
|
} else {
|
||||||
|
privateTx = types.NewTransaction(tx.Nonce(), c.address, tx.Value(), tx.Gas(), tx.GasPrice(), payload)
|
||||||
|
}
|
||||||
|
privateTx.SetPrivate()
|
||||||
|
return privateTx
|
||||||
|
}
|
||||||
|
|
||||||
// ensureContext is a helper method to ensure a context is not nil, even if the
|
// ensureContext is a helper method to ensure a context is not nil, even if the
|
||||||
// user specified it as such.
|
// user specified it as such.
|
||||||
func ensureContext(ctx context.Context) context.Context {
|
func ensureContext(ctx context.Context) context.Context {
|
||||||
|
|
|
@ -76,7 +76,7 @@ func TestWaitDeployed(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Send and mine the transaction.
|
// Send and mine the transaction.
|
||||||
backend.SendTransaction(ctx, tx)
|
backend.SendTransaction(ctx, tx, bind.PrivateTxArgs{})
|
||||||
backend.Commit()
|
backend.Commit()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|
|
@ -41,6 +41,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"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"
|
||||||
|
@ -483,7 +485,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Submit the transaction and mark as funded if successful
|
// Submit the transaction and mark as funded if successful
|
||||||
if err := f.client.SendTransaction(context.Background(), signed); err != nil {
|
if err := f.client.SendTransaction(context.Background(), signed, bind.PrivateTxArgs{}); err != nil {
|
||||||
f.lock.Unlock()
|
f.lock.Unlock()
|
||||||
if err = sendError(conn, err); err != nil {
|
if err = sendError(conn, err); err != nil {
|
||||||
log.Warn("Failed to send transaction transmission error to client", "err", err)
|
log.Warn("Failed to send transaction transmission error to client", "err", err)
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Abigen with Quorum
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
Abigen is a source code generator that converts smart contract ABI definitions into type-safe Go packages. In addition to the original capabilities provided by Ethereum described [here](https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts). Quorum Abigen also supports private transactions.
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
`PrivateFrom` and `PrivateFor` fields have been added to the `bind.TransactOpts` which allows users to specify the public keys of the transaction manager (Tessera/Constellation) used to send and receive private transactions. The existing `ethclient` has been extended with a private transaction manager client to support sending `/storeraw` request.
|
|
@ -24,6 +24,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
@ -35,6 +37,7 @@ import (
|
||||||
// Client defines typed wrappers for the Ethereum RPC API.
|
// Client defines typed wrappers for the Ethereum RPC API.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
c *rpc.Client
|
c *rpc.Client
|
||||||
|
pc privateTransactionManagerClient // Tessera/Constellation client
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dial connects a client to the given URL.
|
// Dial connects a client to the given URL.
|
||||||
|
@ -52,7 +55,19 @@ func DialContext(ctx context.Context, rawurl string) (*Client, error) {
|
||||||
|
|
||||||
// NewClient creates a client that uses the given RPC client.
|
// NewClient creates a client that uses the given RPC client.
|
||||||
func NewClient(c *rpc.Client) *Client {
|
func NewClient(c *rpc.Client) *Client {
|
||||||
return &Client{c}
|
return &Client{c, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quorum
|
||||||
|
//
|
||||||
|
// provides support for private transactions
|
||||||
|
func (ec *Client) WithPrivateTransactionManager(rawurl string) (*Client, error) {
|
||||||
|
var err error
|
||||||
|
ec.pc, err = newPrivateTransactionManagerClient(rawurl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *Client) Close() {
|
func (ec *Client) Close() {
|
||||||
|
@ -498,13 +513,27 @@ func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64
|
||||||
//
|
//
|
||||||
// If the transaction was a contract creation use the TransactionReceipt method to get the
|
// If the transaction was a contract creation use the TransactionReceipt method to get the
|
||||||
// contract address after the transaction has been mined.
|
// contract address after the transaction has been mined.
|
||||||
func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction, args bind.PrivateTxArgs) error {
|
||||||
data, err := rlp.EncodeToBytes(tx)
|
data, err := rlp.EncodeToBytes(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if args.PrivateFor != nil {
|
||||||
|
return ec.c.CallContext(ctx, nil, "eth_sendRawPrivateTransaction", common.ToHex(data), bind.PrivateTxArgs{PrivateFor: args.PrivateFor})
|
||||||
|
} else {
|
||||||
return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data))
|
return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quorum
|
||||||
|
//
|
||||||
|
// Retrieve encrypted payload hash from the private transaction manager if configured
|
||||||
|
func (ec *Client) PreparePrivateTransaction(data []byte, privateFrom string) ([]byte, error) {
|
||||||
|
if ec.pc == nil {
|
||||||
|
return nil, errors.New("missing private transaction manager client configuration")
|
||||||
|
}
|
||||||
|
return ec.pc.storeRaw(data, privateFrom)
|
||||||
|
}
|
||||||
|
|
||||||
func toCallArg(msg ethereum.CallMsg) interface{} {
|
func toCallArg(msg ethereum.CallMsg) interface{} {
|
||||||
arg := map[string]interface{}{
|
arg := map[string]interface{}{
|
||||||
|
|
|
@ -22,6 +22,8 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
@ -150,3 +152,30 @@ func TestToFilterArg(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClient_PreparePrivateTransaction_whenTypical(t *testing.T) {
|
||||||
|
testObject := NewClient(nil)
|
||||||
|
|
||||||
|
_, err := testObject.PreparePrivateTransaction([]byte("arbitrary payload"), "arbitrary private from")
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_PreparePrivateTransaction_whenClientIsConfigured(t *testing.T) {
|
||||||
|
expectedData := []byte("arbitrary data")
|
||||||
|
testObject := NewClient(nil)
|
||||||
|
testObject.pc = &privateTransactionManagerStubClient{expectedData}
|
||||||
|
|
||||||
|
actualData, err := testObject.PreparePrivateTransaction([]byte("arbitrary payload"), "arbitrary private from")
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expectedData, actualData)
|
||||||
|
}
|
||||||
|
|
||||||
|
type privateTransactionManagerStubClient struct {
|
||||||
|
expectedData []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *privateTransactionManagerStubClient) storeRaw(data []byte, privateFrom string) ([]byte, error) {
|
||||||
|
return s.expectedData, nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package ethclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
type privateTransactionManagerClient interface {
|
||||||
|
storeRaw(data []byte, privateFrom string) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type privateTransactionManagerDefaultClient struct {
|
||||||
|
rawurl string
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new client to interact with private transaction manager via a HTTP endpoint
|
||||||
|
func newPrivateTransactionManagerClient(endpoint string) (privateTransactionManagerClient, error) {
|
||||||
|
_, err := url.Parse(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &privateTransactionManagerDefaultClient{
|
||||||
|
rawurl: endpoint,
|
||||||
|
httpClient: &http.Client{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type storeRawReq struct {
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
From string `json:"from,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type storeRawResp struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *privateTransactionManagerDefaultClient) storeRaw(data []byte, privateFrom string) ([]byte, error) {
|
||||||
|
storeRawReq := &storeRawReq{
|
||||||
|
Payload: base64.StdEncoding.EncodeToString(data),
|
||||||
|
From: privateFrom,
|
||||||
|
}
|
||||||
|
reqBodyBuf := new(bytes.Buffer)
|
||||||
|
if err := json.NewEncoder(reqBodyBuf).Encode(storeRawReq); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := pc.httpClient.Post(pc.rawurl+"/storeraw", "application/json", reqBodyBuf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to invoke /storeraw due to %s", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("server returns %s", resp.Status)
|
||||||
|
}
|
||||||
|
// parse response
|
||||||
|
var storeRawResp storeRawResp
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&storeRawResp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encryptedPayloadHash, err := base64.StdEncoding.DecodeString(storeRawResp.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return encryptedPayloadHash, nil
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package ethclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
arbitraryBase64Data = "YXJiaXRyYXJ5IGRhdGE=" // = "arbitrary data"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPrivateTransactionManagerClient_storeRaw(t *testing.T) {
|
||||||
|
// mock tessera client
|
||||||
|
arbitraryServer := newStoreRawServer()
|
||||||
|
defer arbitraryServer.Close()
|
||||||
|
testObject, err := newPrivateTransactionManagerClient(arbitraryServer.URL)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
key, err := testObject.storeRaw([]byte("arbitrary payload"), "arbitrary private from")
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "arbitrary data", string(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStoreRawServer() *httptest.Server {
|
||||||
|
arbitraryResponse := fmt.Sprintf(`
|
||||||
|
{
|
||||||
|
"key": "%s"
|
||||||
|
}
|
||||||
|
`, arbitraryBase64Data)
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/storeraw", func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
if req.Method == "POST" {
|
||||||
|
// parse request
|
||||||
|
var storeRawReq storeRawReq
|
||||||
|
if err := json.NewDecoder(req.Body).Decode(&storeRawReq); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// send response
|
||||||
|
_, _ = fmt.Fprintf(w, "%s", arbitraryResponse)
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
return httptest.NewServer(mux)
|
||||||
|
}
|
|
@ -29,9 +29,10 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"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"
|
||||||
|
@ -1592,7 +1593,7 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr
|
||||||
}
|
}
|
||||||
newTx := sendArgs.toTransaction()
|
newTx := sendArgs.toTransaction()
|
||||||
// set v param to 37 to indicate private tx before submitting to the signer.
|
// set v param to 37 to indicate private tx before submitting to the signer.
|
||||||
if len(sendArgs.PrivateFor) > 0 {
|
if sendArgs.PrivateFor != nil {
|
||||||
newTx.SetPrivate()
|
newTx.SetPrivate()
|
||||||
}
|
}
|
||||||
signedTx, err := s.sign(sendArgs.From, newTx)
|
signedTx, err := s.sign(sendArgs.From, newTx)
|
||||||
|
|
|
@ -21,6 +21,8 @@ package geth
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
)
|
)
|
||||||
|
@ -312,5 +314,5 @@ func (ec *EthereumClient) EstimateGas(ctx *Context, msg *CallMsg) (gas int64, _
|
||||||
// If the transaction was a contract creation use the TransactionReceipt method to get the
|
// If the transaction was a contract creation use the TransactionReceipt method to get the
|
||||||
// contract address after the transaction has been mined.
|
// contract address after the transaction has been mined.
|
||||||
func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error {
|
func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error {
|
||||||
return ec.client.SendTransaction(ctx.context, tx.tx)
|
return ec.client.SendTransaction(ctx.context, tx.tx, bind.PrivateTxArgs{})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue