Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support mandatory recipients #1243

Merged
merged 9 commits into from
Sep 7, 2021
39 changes: 27 additions & 12 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1780,9 +1780,10 @@ type PrivateTxArgs struct {
PrivateFrom string `json:"privateFrom"`
// PrivateFor is the list of public keys which are available in the Private Transaction Managers in the network.
// The transaction payload is only visible to those party to the transaction.
PrivateFor []string `json:"privateFor"`
PrivateTxType string `json:"restriction"`
PrivacyFlag engine.PrivacyFlagType `json:"privacyFlag"`
PrivateFor []string `json:"privateFor"`
PrivateTxType string `json:"restriction"`
PrivacyFlag engine.PrivacyFlagType `json:"privacyFlag"`
MandatoryRecipients []string `json:"mandatoryFor"`
}

func (args *PrivateTxArgs) SetDefaultPrivateFrom(ctx context.Context, b Backend) error {
Expand Down Expand Up @@ -2711,6 +2712,16 @@ func checkAndHandlePrivateTransaction(ctx context.Context, b Backend, tx *types.
return
}

if engine.PrivacyFlagMandatoryRecipients == privateTxArgs.PrivacyFlag && len(privateTxArgs.MandatoryRecipients) == 0 {
err = fmt.Errorf("missing mandatory recipients data. if no mandatory recipients required consider using PrivacyFlag=1(PartyProtection)")
return
}

if engine.PrivacyFlagMandatoryRecipients != privateTxArgs.PrivacyFlag && len(privateTxArgs.MandatoryRecipients) > 0 {
err = fmt.Errorf("privacy metadata invalid. mandatory recipients are only applicable for PrivacyFlag=2(MandatoryRecipients)")
return
}

// validate that PrivateFrom is one of the addresses of the private state resolved from the user context
if b.ChainConfig().IsMPS {
var psm *mps.PrivateStateMetadata
Expand Down Expand Up @@ -2761,7 +2772,7 @@ func handlePrivateTransaction(ctx context.Context, b Backend, tx *types.Transact

data := tx.Data()

log.Debug("sending private tx", "txnType", txnType, "data", common.FormatTerminalString(data), "privatefrom", privateTxArgs.PrivateFrom, "privatefor", privateTxArgs.PrivateFor, "privacyFlag", privateTxArgs.PrivacyFlag)
log.Debug("sending private tx", "txnType", txnType, "data", common.FormatTerminalString(data), "privatefrom", privateTxArgs.PrivateFrom, "privatefor", privateTxArgs.PrivateFor, "privacyFlag", privateTxArgs.PrivacyFlag, "mandatoryfor", privateTxArgs.MandatoryRecipients)

switch txnType {
case FillTransaction:
Expand Down Expand Up @@ -2799,9 +2810,10 @@ func handleRawPrivateTransaction(ctx context.Context, b Backend, tx *types.Trans
}

metadata := engine.ExtraMetadata{
ACHashes: affectedCATxHashes,
ACMerkleRoot: merkleRoot,
PrivacyFlag: privateTxArgs.PrivacyFlag,
ACHashes: affectedCATxHashes,
ACMerkleRoot: merkleRoot,
PrivacyFlag: privateTxArgs.PrivacyFlag,
MandatoryRecipients: privateTxArgs.MandatoryRecipients,
}
_, _, data, err = private.P.SendSignedTx(hash, privateTxArgs.PrivateFor, &metadata)
if err != nil {
Expand All @@ -2815,7 +2827,8 @@ func handleRawPrivateTransaction(ctx context.Context, b Backend, tx *types.Trans
"privatefor", privateTxArgs.PrivateFor,
"affectedCATxHashes", metadata.ACHashes,
"merkleroot", metadata.ACHashes,
"privacyflag", metadata.PrivacyFlag)
"privacyflag", metadata.PrivacyFlag,
"mandatoryrecipients", metadata.MandatoryRecipients)
return
}

Expand All @@ -2828,9 +2841,10 @@ func handleNormalPrivateTransaction(ctx context.Context, b Backend, tx *types.Tr
}

metadata := engine.ExtraMetadata{
ACHashes: affectedCATxHashes,
ACMerkleRoot: merkleRoot,
PrivacyFlag: privateTxArgs.PrivacyFlag,
ACHashes: affectedCATxHashes,
ACMerkleRoot: merkleRoot,
PrivacyFlag: privateTxArgs.PrivacyFlag,
MandatoryRecipients: privateTxArgs.MandatoryRecipients,
}
_, _, hash, err = private.P.Send(data, privateTxArgs.PrivateFrom, privateTxArgs.PrivateFor, &metadata)
if err != nil {
Expand All @@ -2844,7 +2858,8 @@ func handleNormalPrivateTransaction(ctx context.Context, b Backend, tx *types.Tr
"privatefor", privateTxArgs.PrivateFor,
"affectedCATxHashes", metadata.ACHashes,
"merkleroot", metadata.ACHashes,
"privacyflag", metadata.PrivacyFlag)
"privacyflag", metadata.PrivacyFlag,
"mandatoryrecipients", metadata.MandatoryRecipients)
return
}

Expand Down
100 changes: 96 additions & 4 deletions internal/ethapi/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ import (
)

var (
arbitraryCtx = context.Background()
arbitraryPrivateFrom = "arbitrary private from"
arbitraryPrivateFor = []string{"arbitrary party 1", "arbitrary party 2"}
privateTxArgs = &PrivateTxArgs{
arbitraryCtx = context.Background()
arbitraryPrivateFrom = "arbitrary private from"
arbitraryPrivateFor = []string{"arbitrary party 1", "arbitrary party 2"}
arbitraryMandatoryFor = []string{"arbitrary party 2"}
privateTxArgs = &PrivateTxArgs{
PrivateFrom: arbitraryPrivateFrom,
PrivateFor: arbitraryPrivateFor,
}
Expand Down Expand Up @@ -531,6 +532,97 @@ func TestHandlePrivateTransaction_whenRawStandardPrivateMessageCall(t *testing.T

}

func TestHandlePrivateTransaction_whenMandatoryRecipients(t *testing.T) {
assert := assert.New(t)
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

mockTM := private.NewMockPrivateTransactionManager(mockCtrl)

saved := private.P
defer func() {
private.P = saved
privateTxArgs.MandatoryRecipients = nil
}()
private.P = mockTM
privateTxArgs.MandatoryRecipients = arbitraryMandatoryFor
privateTxArgs.PrivacyFlag = engine.PrivacyFlagMandatoryRecipients

var capturedMetadata engine.ExtraMetadata

mockTM.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
Do(func(arg1 interface{}, arg2 string, arg3 interface{}, arg4 *engine.ExtraMetadata) {
capturedMetadata = *arg4
}).Times(1)

_, err := handlePrivateTransaction(arbitraryCtx, &StubBackend{}, simpleStorageContractCreationTx, privateTxArgs, arbitraryFrom, NormalTransaction)

assert.NoError(err)
assert.Equal(engine.PrivacyFlagMandatoryRecipients, capturedMetadata.PrivacyFlag)
assert.Equal(arbitraryMandatoryFor, capturedMetadata.MandatoryRecipients)

}

func TestHandlePrivateTransaction_whenRawPrivateWithMandatoryRecipients(t *testing.T) {
assert := assert.New(t)
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

mockTM := private.NewMockPrivateTransactionManager(mockCtrl)

saved := private.P
defer func() {
private.P = saved
privateTxArgs.MandatoryRecipients = nil
}()
private.P = mockTM
privateTxArgs.MandatoryRecipients = arbitraryMandatoryFor

privateTxArgs.PrivacyFlag = engine.PrivacyFlagMandatoryRecipients

var capturedMetadata engine.ExtraMetadata

mockTM.EXPECT().ReceiveRaw(gomock.Any()).Times(1)

mockTM.EXPECT().SendSignedTx(gomock.Any(), gomock.Any(), gomock.Any()).
Do(func(arg1 interface{}, arg2 []string, arg3 *engine.ExtraMetadata) {
capturedMetadata = *arg3
}).Times(1)

_, err := handlePrivateTransaction(arbitraryCtx, &StubBackend{}, simpleStorageContractCreationTx, privateTxArgs, arbitraryFrom, RawTransaction)

assert.NoError(err)
assert.Equal(engine.PrivacyFlagMandatoryRecipients, capturedMetadata.PrivacyFlag)
assert.Equal(arbitraryMandatoryFor, capturedMetadata.MandatoryRecipients)

}

func TestHandlePrivateTransaction_whenMandatoryRecipientsDataInvalid(t *testing.T) {
assert := assert.New(t)

privateTxArgs.PrivacyFlag = engine.PrivacyFlagMandatoryRecipients

_, _, _, err := checkAndHandlePrivateTransaction(arbitraryCtx, &StubBackend{}, simpleStorageContractCreationTx, privateTxArgs, arbitraryFrom, NormalTransaction)

assert.Error(err, "missing mandatory recipients data. if no mandatory recipients required consider using PrivacyFlag=1(PartyProtection)")

}

func TestHandlePrivateTransaction_whenNoMandatoryRecipientsData(t *testing.T) {
assert := assert.New(t)

privateTxArgs.PrivacyFlag = engine.PrivacyFlagPartyProtection
defer func() {
privateTxArgs.MandatoryRecipients = nil
}()
privateTxArgs.MandatoryRecipients = arbitraryMandatoryFor

_, _, _, err := checkAndHandlePrivateTransaction(arbitraryCtx, &StubBackend{}, simpleStorageContractCreationTx, privateTxArgs, arbitraryFrom, NormalTransaction)

assert.Error(err, "privacy metadata invalid. mandatory recipients are only applicable for PrivacyFlag=2(MandatoryRecipients)")

}

func TestSubmitPrivateTransaction(t *testing.T) {
assert := assert.New(t)

Expand Down
15 changes: 10 additions & 5 deletions private/engine/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var (
ErrPrivateTxManagerNotReady = errors.New("private transaction manager is not ready")
ErrPrivateTxManagerNotSupported = errors.New("private transaction manager does not support this operation")
ErrPrivateTxManagerDoesNotSupportPrivacyEnhancements = errors.New("private transaction manager does not support privacy enhancements")
ErrPrivateTxManagerDoesNotSupportMandatoryRecipients = errors.New("private transaction manager does not support mandatory recipients")
)

type PrivacyGroup struct {
Expand All @@ -41,8 +42,10 @@ type ExtraMetadata struct {
// Contract participants that are managed by the corresponding Tessera.
// Being used in Multi Tenancy
ManagedParties []string
// the sender of the transaction
// The sender of the transaction
Sender string
// Recipients that are mandated to be included
MandatoryRecipients []string
}

type Client struct {
Expand All @@ -65,9 +68,10 @@ func (c *Client) Get(path string) (*http.Response, error) {
type PrivacyFlagType uint64

const (
PrivacyFlagStandardPrivate PrivacyFlagType = iota // 0
PrivacyFlagPartyProtection PrivacyFlagType = 1 << PrivacyFlagType(iota-1) // 1
PrivacyFlagStateValidation = iota | PrivacyFlagPartyProtection // 3 which includes PrivacyFlagPartyProtection
PrivacyFlagStandardPrivate PrivacyFlagType = iota
PrivacyFlagPartyProtection
PrivacyFlagMandatoryRecipients
PrivacyFlagStateValidation
)

func (f PrivacyFlagType) IsNotStandardPrivate() bool {
Expand All @@ -91,7 +95,7 @@ func (f PrivacyFlagType) HasAll(others ...PrivacyFlagType) bool {
}

func (f PrivacyFlagType) Validate() error {
if f == PrivacyFlagStandardPrivate || f == PrivacyFlagPartyProtection || f == PrivacyFlagStateValidation {
if f == PrivacyFlagStandardPrivate || f == PrivacyFlagPartyProtection || f == PrivacyFlagMandatoryRecipients || f == PrivacyFlagStateValidation {
return nil
}
return fmt.Errorf("invalid privacy flag")
Expand All @@ -104,6 +108,7 @@ const (
PrivacyEnhancements PrivateTransactionManagerFeature = 1 << PrivateTransactionManagerFeature(iota-1) // 1
MultiTenancy PrivateTransactionManagerFeature = 1 << PrivateTransactionManagerFeature(iota-1) // 2
MultiplePrivateStates PrivateTransactionManagerFeature = 1 << PrivateTransactionManagerFeature(iota-1) // 4
MandatoryRecipients PrivateTransactionManagerFeature = 1 << PrivateTransactionManagerFeature(iota-1) // 8
)

type FeatureSet struct {
Expand Down
19 changes: 19 additions & 0 deletions private/engine/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ func TestPrivacyFlag_whenPrivateStateValidation(t *testing.T) {
assert.True(PrivacyFlagStateValidation.Has(PrivacyFlagPartyProtection), "State Validation must have party protection by default")
}

func TestPrivacyFlag_whenMandatoryRecipients(t *testing.T) {
assert := assert.New(t)

flag := PrivacyFlagMandatoryRecipients

assert.NoError(flag.Validate())
assert.True(flag.Has(PrivacyFlagMandatoryRecipients))
assert.True(PrivacyFlagStateValidation.Has(flag))

}

func TestPrivacyFlagType_Validate_whenSuccess(t *testing.T) {
assert := assert.New(t)

Expand All @@ -69,3 +80,11 @@ func TestPrivacyFlagType_Validate_whenFailure(t *testing.T) {

assert.Error(flag.Validate())
}

func TestFeatureSet_HasFeature(t *testing.T) {
assert := assert.New(t)

featureSet := NewFeatureSet(PrivacyEnhancements, MultiTenancy, MultiplePrivateStates, MandatoryRecipients)
assert.True(featureSet.HasFeature(MandatoryRecipients))
assert.True(featureSet.HasFeature(MultiplePrivateStates))
}
4 changes: 4 additions & 0 deletions private/engine/tessera/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type sendRequest struct {
ExecHash string `json:"execHash,omitempty"`

PrivacyFlag engine.PrivacyFlagType `json:"privacyFlag"`

MandatoryRecipients []string `json:"mandatoryRecipients"`
}

// request object for /send API
Expand Down Expand Up @@ -64,6 +66,8 @@ type sendSignedTxRequest struct {
ExecHash string `json:"execHash,omitempty"`

PrivacyFlag engine.PrivacyFlagType `json:"privacyFlag"`

MandatoryRecipients []string `json:"mandatoryRecipients"`
}

type sendSignedTxResponse struct {
Expand Down
11 changes: 11 additions & 0 deletions private/engine/tessera/tessera.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ func (t *tesseraPrivateTxManager) submitJSON(method, path string, request interf
if t.features.HasFeature(engine.MultiTenancy) {
apiVersion = "vnd.tessera-2.1+"
}
if t.features.HasFeature(engine.MandatoryRecipients) && (path == "/send" || path == "/sendsignedtx") {
apiVersion = "vnd.tessera-4.0+"
}
if t.features.HasFeature(engine.MultiplePrivateStates) && path == "/groups/resident" {
// for the groups API the Content-type/Accept is application/json
apiVersion = ""
Expand Down Expand Up @@ -95,6 +98,9 @@ func (t *tesseraPrivateTxManager) Send(data []byte, from string, to []string, ex
if extra.PrivacyFlag.IsNotStandardPrivate() && !t.features.HasFeature(engine.PrivacyEnhancements) {
return "", nil, common.EncryptedPayloadHash{}, engine.ErrPrivateTxManagerDoesNotSupportPrivacyEnhancements
}
if extra.PrivacyFlag == engine.PrivacyFlagMandatoryRecipients && !t.features.HasFeature(engine.MandatoryRecipients) {
return "", nil, common.EncryptedPayloadHash{}, engine.ErrPrivateTxManagerDoesNotSupportMandatoryRecipients
}
response := new(sendResponse)
acMerkleRoot := ""
if !common.EmptyHash(extra.ACMerkleRoot) {
Expand All @@ -107,6 +113,7 @@ func (t *tesseraPrivateTxManager) Send(data []byte, from string, to []string, ex
AffectedContractTransactions: extra.ACHashes.ToBase64s(),
ExecHash: acMerkleRoot,
PrivacyFlag: extra.PrivacyFlag,
MandatoryRecipients: extra.MandatoryRecipients,
}, response); err != nil {
return "", nil, common.EncryptedPayloadHash{}, err
}
Expand Down Expand Up @@ -218,6 +225,9 @@ func (t *tesseraPrivateTxManager) SendSignedTx(data common.EncryptedPayloadHash,
if extra.PrivacyFlag.IsNotStandardPrivate() && !t.features.HasFeature(engine.PrivacyEnhancements) {
return "", nil, nil, engine.ErrPrivateTxManagerDoesNotSupportPrivacyEnhancements
}
if extra.PrivacyFlag == engine.PrivacyFlagMandatoryRecipients && !t.features.HasFeature(engine.MandatoryRecipients) {
return "", nil, nil, engine.ErrPrivateTxManagerDoesNotSupportMandatoryRecipients
}
response := new(sendSignedTxResponse)
acMerkleRoot := ""
if !common.EmptyHash(extra.ACMerkleRoot) {
Expand All @@ -232,6 +242,7 @@ func (t *tesseraPrivateTxManager) SendSignedTx(data common.EncryptedPayloadHash,
AffectedContractTransactions: extra.ACHashes.ToBase64s(),
ExecHash: acMerkleRoot,
PrivacyFlag: extra.PrivacyFlag,
MandatoryRecipients: extra.MandatoryRecipients,
}, response); err != nil {
return "", nil, nil, err
}
Expand Down
Loading