Skip to content

Commit 18721a7

Browse files
authored
Merge pull request #1231 from SatpalSandhu61/consensysengineering-privacy-support-using-precompile-v3
Privacy Precompile: enables private transactions with extended obfuscated fields
2 parents 0c52059 + 39a6dca commit 18721a7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+3120
-556
lines changed

accounts/abi/bind/backend.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ type ContractTransactor interface {
8484
SendTransaction(ctx context.Context, tx *types.Transaction, args PrivateTxArgs) error
8585
// PreparePrivateTransaction send the private transaction to Tessera/Constellation's /storeraw API using HTTP
8686
PreparePrivateTransaction(data []byte, privateFrom string) (common.EncryptedPayloadHash, error)
87+
// DistributeTransaction distributes the private transaction payload to its private recipients, and sends the
88+
// private transaction to the nodes PTM, returning a PTM hash for the Private Marker Transaction
89+
DistributeTransaction(ctx context.Context, tx *types.Transaction, args PrivateTxArgs) (string, error)
8790
}
8891

8992
// ContractFilterer defines the methods needed to access log events using one-off

accounts/abi/bind/backends/simulated.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,10 @@ func (b *SimulatedBackend) PreparePrivateTransaction(data []byte, privateFrom st
602602
return common.EncryptedPayloadHash{}, nil
603603
}
604604

605+
func (b *SimulatedBackend) DistributeTransaction(ctx context.Context, tx *types.Transaction, args bind.PrivateTxArgs) (string, error) {
606+
return tx.Hash().String(), nil
607+
}
608+
605609
// FilterLogs executes a log filter operation, blocking during execution and
606610
// returning all the results in one batch.
607611
//

accounts/abi/bind/base.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ type TransactOpts struct {
6363
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
6464

6565
// Quorum
66-
PrivateFrom string // The public key of the Tessera/Constellation identity to send this tx from.
67-
PrivateFor []string // The public keys of the Tessera/Constellation identities this tx is intended for.
66+
PrivateFrom string // The public key of the Tessera/Constellation identity to send this tx from.
67+
PrivateFor []string // The public keys of the Tessera/Constellation identities this tx is intended for.
68+
IsUsingPrivacyPrecompile bool
6869
}
6970

7071
// FilterOpts is the collection of options to fine tune filtering for events
@@ -276,6 +277,11 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
276277
}
277278
payload = hash.Bytes()
278279
rawTx = c.createPrivateTransaction(rawTx, payload)
280+
281+
if opts.IsUsingPrivacyPrecompile {
282+
rawTx, _ = c.createMarkerTx(opts, rawTx, PrivateTxArgs{PrivateFor: opts.PrivateFor})
283+
opts.PrivateFor = nil
284+
}
279285
}
280286

281287
// Choose signer to sign transaction
@@ -424,6 +430,25 @@ func (c *BoundContract) createPrivateTransaction(tx *types.Transaction, payload
424430
return privateTx
425431
}
426432

433+
// (Quorum) createMarkerTx creates a new public privacy marker transaction for the given private tx, distributing tx to the specified privateFor recipients
434+
func (c *BoundContract) createMarkerTx(opts *TransactOpts, tx *types.Transaction, args PrivateTxArgs) (*types.Transaction, error) {
435+
// Choose signer to sign transaction
436+
if opts.Signer == nil {
437+
return nil, errors.New("no signer to authorize the transaction with")
438+
}
439+
signedTx, err := opts.Signer(types.QuorumPrivateTxSigner{}, opts.From, tx)
440+
if err != nil {
441+
return nil, err
442+
}
443+
444+
hash, err := c.transactor.DistributeTransaction(ensureContext(opts.Context), signedTx, args)
445+
if err != nil {
446+
return nil, err
447+
}
448+
449+
return types.NewTransaction(signedTx.Nonce(), common.QuorumPrivacyPrecompileContractAddress(), tx.Value(), tx.Gas(), tx.GasPrice(), common.FromHex(hash)), nil
450+
}
451+
427452
// ensureContext is a helper method to ensure a context is not nil, even if the
428453
// user specified it as such.
429454
func ensureContext(ctx context.Context) context.Context {
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
package bind
2+
3+
import (
4+
"context"
5+
"math/big"
6+
"testing"
7+
8+
"github.com/ethereum/go-ethereum"
9+
"github.com/ethereum/go-ethereum/accounts/abi"
10+
"github.com/ethereum/go-ethereum/common"
11+
"github.com/ethereum/go-ethereum/core/types"
12+
"github.com/ethereum/go-ethereum/crypto"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
var (
17+
tmPrivatePayloadHash common.EncryptedPayloadHash
18+
tmPrivateTxHash common.EncryptedPayloadHash
19+
)
20+
21+
func init() {
22+
payloadHash := crypto.Keccak512([]byte("encrypted-private-payload"))
23+
privateTxHash := crypto.Keccak512([]byte("encrypted-private-tx"))
24+
for i := 0; i < 64; i++ {
25+
tmPrivatePayloadHash[i] = payloadHash[i]
26+
tmPrivateTxHash[i] = privateTxHash[i]
27+
}
28+
}
29+
30+
func TestBoundContract_Transact_ContractCreation_PrivateTransaction(t *testing.T) {
31+
transactor := &mockTransactor{}
32+
33+
c := NewBoundContract(common.Address{}, abi.ABI{}, nil, transactor, nil)
34+
35+
senderNonce := 1
36+
37+
opts := &TransactOpts{
38+
Nonce: big.NewInt(int64(senderNonce)),
39+
PrivateFor: []string{"tm1"},
40+
41+
// arbitrary values to skip the logic we're not testing
42+
GasPrice: big.NewInt(0),
43+
GasLimit: uint64(1),
44+
Signer: passthroughSigner,
45+
}
46+
47+
tx, err := c.transact(opts, nil, nil)
48+
49+
wantNonce := uint64(senderNonce)
50+
wantTo := (*common.Address)(nil)
51+
wantData := tmPrivatePayloadHash.Bytes()
52+
53+
require.NoError(t, err)
54+
require.NotNil(t, tx)
55+
require.Equal(t, wantNonce, tx.Nonce())
56+
require.Equal(t, wantTo, tx.To())
57+
require.Equal(t, wantData, tx.Data())
58+
require.True(t, tx.IsPrivate())
59+
}
60+
61+
func TestBoundContract_Transact_ContractCreation_PrivacyPrecompile(t *testing.T) {
62+
transactor := &mockTransactor{}
63+
64+
c := NewBoundContract(common.Address{}, abi.ABI{}, nil, transactor, nil)
65+
66+
senderNonce := 1
67+
68+
opts := &TransactOpts{
69+
Nonce: big.NewInt(int64(senderNonce)),
70+
PrivateFor: []string{"tm1"},
71+
IsUsingPrivacyPrecompile: true,
72+
73+
// arbitrary values to skip the logic we're not testing
74+
GasPrice: big.NewInt(0),
75+
GasLimit: uint64(1),
76+
Signer: passthroughSigner,
77+
}
78+
79+
pmt, err := c.transact(opts, nil, nil)
80+
81+
require.NoError(t, err)
82+
require.NotNil(t, pmt)
83+
84+
// verify the private marker transaction
85+
wantPMTNonce := uint64(senderNonce)
86+
wantPMTTo := common.QuorumPrivacyPrecompileContractAddress()
87+
wantPMTData := tmPrivateTxHash.Bytes()
88+
89+
require.Equal(t, wantPMTNonce, pmt.Nonce())
90+
require.Equal(t, &wantPMTTo, pmt.To())
91+
require.Equal(t, wantPMTData, pmt.Data())
92+
require.False(t, pmt.IsPrivate())
93+
94+
// verify the captured internal private transaction
95+
pvtTx := transactor.capturedInternalPrivateTransaction
96+
pvtTxArgs := transactor.capturedInternalPrivateTransactionArgs
97+
98+
wantPvtTxNonce := uint64(senderNonce)
99+
wantPvtTxTo := (*common.Address)(nil)
100+
wantPvtTxData := tmPrivatePayloadHash.Bytes()
101+
102+
require.NotNil(t, pvtTx)
103+
require.Equal(t, wantPvtTxNonce, pvtTx.Nonce())
104+
require.Equal(t, wantPvtTxTo, pvtTx.To())
105+
require.Equal(t, wantPvtTxData, pvtTx.Data())
106+
require.True(t, pvtTx.IsPrivate())
107+
108+
require.Equal(t, []string{"tm1"}, pvtTxArgs.PrivateFor)
109+
}
110+
111+
func TestBoundContract_Transact_Transaction_PrivateTransaction(t *testing.T) {
112+
transactor := &mockTransactor{}
113+
114+
contractAddr := common.HexToAddress("0x1932c48b2bf8102ba33b4a6b545c32236e342f34")
115+
c := NewBoundContract(contractAddr, abi.ABI{}, nil, transactor, nil)
116+
117+
senderNonce := 1
118+
119+
opts := &TransactOpts{
120+
Nonce: big.NewInt(int64(senderNonce)),
121+
PrivateFor: []string{"tm1"},
122+
123+
// arbitrary values to skip the logic we're not testing
124+
GasPrice: big.NewInt(0),
125+
GasLimit: uint64(1),
126+
Signer: passthroughSigner,
127+
}
128+
129+
tx, err := c.transact(opts, &contractAddr, nil)
130+
131+
wantNonce := uint64(senderNonce)
132+
wantTo := &contractAddr
133+
wantData := tmPrivatePayloadHash.Bytes()
134+
135+
require.NoError(t, err)
136+
require.NotNil(t, tx)
137+
require.Equal(t, wantNonce, tx.Nonce())
138+
require.Equal(t, wantTo, tx.To())
139+
require.Equal(t, wantData, tx.Data())
140+
require.True(t, tx.IsPrivate())
141+
}
142+
143+
func TestBoundContract_Transact_Transaction_PrivacyPrecompile(t *testing.T) {
144+
transactor := &mockTransactor{}
145+
146+
contractAddr := common.HexToAddress("0x1932c48b2bf8102ba33b4a6b545c32236e342f34")
147+
c := NewBoundContract(contractAddr, abi.ABI{}, nil, transactor, nil)
148+
149+
senderNonce := 1
150+
151+
opts := &TransactOpts{
152+
Nonce: big.NewInt(int64(senderNonce)),
153+
PrivateFor: []string{"tm1"},
154+
IsUsingPrivacyPrecompile: true,
155+
156+
// arbitrary values to skip the logic we're not testing
157+
GasPrice: big.NewInt(0),
158+
GasLimit: uint64(1),
159+
Signer: passthroughSigner,
160+
}
161+
162+
pmt, err := c.transact(opts, &contractAddr, nil)
163+
164+
require.NoError(t, err)
165+
require.NotNil(t, pmt)
166+
167+
// verify the private marker transaction
168+
wantPMTNonce := uint64(senderNonce)
169+
wantPMTTo := common.QuorumPrivacyPrecompileContractAddress()
170+
wantPMTData := tmPrivateTxHash.Bytes()
171+
172+
require.Equal(t, wantPMTNonce, pmt.Nonce())
173+
require.Equal(t, &wantPMTTo, pmt.To())
174+
require.Equal(t, wantPMTData, pmt.Data())
175+
require.False(t, pmt.IsPrivate())
176+
177+
// verify the captured internal private transaction
178+
pvtTx := transactor.capturedInternalPrivateTransaction
179+
pvtTxArgs := transactor.capturedInternalPrivateTransactionArgs
180+
181+
wantPvtTxNonce := uint64(senderNonce)
182+
wantPvtTxTo := &contractAddr
183+
wantPvtTxData := tmPrivatePayloadHash.Bytes()
184+
185+
require.NotNil(t, pvtTx)
186+
require.Equal(t, wantPvtTxNonce, pvtTx.Nonce())
187+
require.Equal(t, wantPvtTxTo, pvtTx.To())
188+
require.Equal(t, wantPvtTxData, pvtTx.Data())
189+
require.True(t, pvtTx.IsPrivate())
190+
191+
require.Equal(t, []string{"tm1"}, pvtTxArgs.PrivateFor)
192+
}
193+
194+
func passthroughSigner(_ types.Signer, _ common.Address, tx *types.Transaction) (*types.Transaction, error) {
195+
return tx, nil
196+
}
197+
198+
type mockTransactor struct {
199+
capturedInternalPrivateTransaction *types.Transaction
200+
capturedInternalPrivateTransactionArgs PrivateTxArgs
201+
}
202+
203+
func (s *mockTransactor) PreparePrivateTransaction(_ []byte, _ string) (common.EncryptedPayloadHash, error) {
204+
return tmPrivatePayloadHash, nil
205+
}
206+
207+
func (s *mockTransactor) DistributeTransaction(_ context.Context, tx *types.Transaction, args PrivateTxArgs) (string, error) {
208+
s.capturedInternalPrivateTransaction = tx
209+
s.capturedInternalPrivateTransactionArgs = args
210+
return tmPrivateTxHash.Hex(), nil
211+
}
212+
213+
func (s *mockTransactor) SendTransaction(_ context.Context, _ *types.Transaction, _ PrivateTxArgs) error {
214+
return nil
215+
}
216+
217+
func (s *mockTransactor) PendingCodeAt(_ context.Context, _ common.Address) ([]byte, error) {
218+
panic("implement me")
219+
}
220+
221+
func (s *mockTransactor) PendingNonceAt(_ context.Context, _ common.Address) (uint64, error) {
222+
panic("implement me")
223+
}
224+
225+
func (s *mockTransactor) SuggestGasPrice(_ context.Context) (*big.Int, error) {
226+
panic("implement me")
227+
}
228+
229+
func (s *mockTransactor) EstimateGas(_ context.Context, _ ethereum.CallMsg) (gas uint64, err error) {
230+
panic("implement me")
231+
}

cmd/geth/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ func checkWhisper(ctx *cli.Context) {
171171
func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
172172
stack, cfg := makeConfigNode(ctx)
173173

174+
//Must occur before registering the extension service, as it needs an initialised PTM to be enabled
175+
if err := quorumInitialisePrivacy(ctx); err != nil {
176+
utils.Fatalf("Error initialising Private Transaction Manager: %s", err.Error())
177+
}
178+
174179
// Quorum - returning `ethService` too for the Raft and extension service
175180
backend, ethService := utils.RegisterEthService(stack, &cfg.Eth)
176181

cmd/geth/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ var (
182182
utils.EVMCallTimeOutFlag,
183183
utils.MultitenancyFlag,
184184
utils.RevertReasonFlag,
185+
utils.QuorumEnablePrivacyMarker,
185186
utils.QuorumPTMUnixSocketFlag,
186187
utils.QuorumPTMUrlFlag,
187188
utils.QuorumPTMTimeoutFlag,

cmd/geth/retesteth.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ func (api *RetestethAPI) mineBlock() error {
527527
statedb, pvtstdb,
528528
header, tx, &header.GasUsed, *api.blockchain.GetVMConfig(),
529529
false,
530+
nil,
530531
)
531532
if err != nil {
532533
statedb.RevertToSnapshot(snap)

cmd/geth/usage.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
260260
utils.MultitenancyFlag,
261261
utils.RevertReasonFlag,
262262
utils.PrivateCacheTrieJournalFlag,
263+
utils.QuorumEnablePrivacyMarker,
263264
},
264265
},
265266
{

cmd/utils/flags.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,11 @@ var (
866866
Value: eth.DefaultConfig.PrivateTrieCleanCacheJournal,
867867
}
868868

869+
QuorumEnablePrivacyMarker = cli.BoolFlag{
870+
Name: "privacymarker.enable",
871+
Usage: "Enable use of privacy marker transactions (PMT) for this node.",
872+
}
873+
869874
// Quorum Private Transaction Manager connection options
870875
QuorumPTMUnixSocketFlag = DirectoryFlag{
871876
Name: "ptm.socket",
@@ -1710,6 +1715,8 @@ func setQuorumConfig(ctx *cli.Context, cfg *eth.Config) error {
17101715
cfg.EVMCallTimeOut = time.Duration(ctx.GlobalInt(EVMCallTimeOutFlag.Name)) * time.Second
17111716
cfg.EnableMultitenancy = ctx.GlobalBool(MultitenancyFlag.Name)
17121717
cfg.SaveRevertReason = ctx.GlobalBool(RevertReasonFlag.Name)
1718+
cfg.QuorumPrivacyMarkerTransactionsEnabled = ctx.GlobalBool(QuorumEnablePrivacyMarker.Name)
1719+
17131720
setIstanbul(ctx, cfg)
17141721
setRaft(ctx, cfg)
17151722
if ctx.GlobalIsSet(PrivateCacheTrieJournalFlag.Name) {

0 commit comments

Comments
 (0)