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

Contract extension with mandatory recipients #1252

Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions extension/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,18 @@ func (service *PrivacyService) watchForCompletionEvents(psi types.PrivateStateId
}
extraMetaData.ACMerkleRoot = storageRoot
}
// Fetch mandatory recipients data from Tessera - only when privacy flag is 2
if privacyMetaData.PrivacyFlag == engine.PrivacyFlagMandatoryRecipients {
fetchedMandatoryRecipients, err := service.ptm.GetMandatory(privacyMetaData.CreationTxHash)
if err != nil || len(fetchedMandatoryRecipients) == 0 {
log.Error("Extension: Unable to fetch mandatory parties for extension management contract", "error", err)
return
}
log.Debug("Extension: able to fetch mandatory recipients", "mandatory", fetchedMandatoryRecipients)
extraMetaData.MandatoryRecipients = fetchedMandatoryRecipients
}
}

_, _, hashOfStateData, err := service.ptm.Send(entireStateData, privateFrom, fetchedParties, &extraMetaData)

if err != nil {
Expand Down
24 changes: 22 additions & 2 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1531,12 +1531,32 @@ func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr
}

// Quorum
func (s *PublicTransactionPoolAPI) GetContractPrivacyMetadata(ctx context.Context, address common.Address) (*state.PrivacyMetadata, error) {

type PrivacyMetadataWithMandatoryRecipients struct {
*state.PrivacyMetadata
MandatoryRecipients []string `json:"mandatoryFor,omitempty"`
}

func (s *PublicTransactionPoolAPI) GetContractPrivacyMetadata(ctx context.Context, address common.Address) (*PrivacyMetadataWithMandatoryRecipients, error) {
state, _, err := s.b.StateAndHeaderByNumber(ctx, rpc.LatestBlockNumber)
if state == nil || err != nil {
return nil, err
}
return state.GetPrivacyMetadata(address)
var mandatoryRecipients []string

privacyMetadata, err := state.GetPrivacyMetadata(address)
if privacyMetadata == nil || err != nil {
return nil, err
}

if privacyMetadata.PrivacyFlag == engine.PrivacyFlagMandatoryRecipients {
mandatoryRecipients, err = private.P.GetMandatory(privacyMetadata.CreationTxHash)
if len(mandatoryRecipients) == 0 || err != nil {
return nil, err
}
}

return &PrivacyMetadataWithMandatoryRecipients{privacyMetadata, mandatoryRecipients}, nil
}

// End Quorum
Expand Down
82 changes: 78 additions & 4 deletions internal/ethapi/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ var (
Data: (*hexutil.Bytes)(arbitraryEmptyData),
}

arbitrarySimpleStorageContractEncryptedPayloadHash = common.BytesToEncryptedPayloadHash([]byte("arbitrary payload hash"))
arbitrarySimpleStorageContractEncryptedPayloadHash = common.BytesToEncryptedPayloadHash([]byte("arbitrary payload hash"))
arbitraryMandatoryRecipientsContractEncryptedPayloadHash = common.BytesToEncryptedPayloadHash([]byte("arbitrary payload hash of tx with mr"))

simpleStorageContractCreationTx = types.NewContractCreation(
0,
Expand All @@ -80,8 +81,9 @@ var (
big.NewInt(0),
arbitrarySimpleStorageContractEncryptedPayloadHash.Bytes())

arbitrarySimpleStorageContractAddress common.Address
arbitraryStandardPrivateSimpleStorageContractAddress common.Address
arbitrarySimpleStorageContractAddress common.Address
arbitraryStandardPrivateSimpleStorageContractAddress common.Address
arbitraryMandatoryRecipientsSimpleStorageContractAddress common.Address

simpleStorageContractMessageCallTx *types.Transaction
standardPrivateSimpleStorageContractMessageCallTx *types.Transaction
Expand Down Expand Up @@ -623,6 +625,69 @@ func TestHandlePrivateTransaction_whenNoMandatoryRecipientsData(t *testing.T) {

}

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

keystore, _, _ := createKeystore(t)

stbBackend := &StubBackend{}
stbBackend.multitenancySupported = false
stbBackend.isPrivacyMarkerTransactionCreationEnabled = false
stbBackend.ks = keystore
stbBackend.accountManager = accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: true}, stbBackend)
stbBackend.poolNonce = 999

public := NewPublicTransactionPoolAPI(stbBackend, nil)

privacyMetadata, _ := public.GetContractPrivacyMetadata(arbitraryCtx, arbitrarySimpleStorageContractAddress)

assert.Equal(engine.PrivacyFlagPartyProtection, privacyMetadata.PrivacyFlag)
assert.Equal(arbitrarySimpleStorageContractEncryptedPayloadHash, privacyMetadata.CreationTxHash)
assert.Equal(0, len(privacyMetadata.MandatoryRecipients))
}

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

keystore, _, _ := createKeystore(t)

stbBackend := &StubBackend{}
stbBackend.multitenancySupported = false
stbBackend.isPrivacyMarkerTransactionCreationEnabled = false
stbBackend.ks = keystore
stbBackend.accountManager = accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: true}, stbBackend)
stbBackend.poolNonce = 999

public := NewPublicTransactionPoolAPI(stbBackend, nil)

mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

mockTM := private.NewMockPrivateTransactionManager(mockCtrl)

saved := private.P
defer func() {
private.P = saved
}()
private.P = mockTM

var capturedTxHash common.EncryptedPayloadHash

mockTM.EXPECT().GetMandatory(gomock.Any()).
DoAndReturn(func(arg1 common.EncryptedPayloadHash) ([]string, error) {
capturedTxHash = arg1
return arbitraryMandatoryFor, nil
}).Times(1)

privacyMetadata, _ := public.GetContractPrivacyMetadata(arbitraryCtx, arbitraryMandatoryRecipientsSimpleStorageContractAddress)

assert.Equal(arbitraryMandatoryRecipientsContractEncryptedPayloadHash, capturedTxHash)

assert.Equal(engine.PrivacyFlagMandatoryRecipients, privacyMetadata.PrivacyFlag)
assert.Equal(arbitraryMandatoryRecipientsContractEncryptedPayloadHash, privacyMetadata.CreationTxHash)
assert.Equal(arbitraryMandatoryFor, privacyMetadata.MandatoryRecipients)
}

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

Expand Down Expand Up @@ -1107,7 +1172,16 @@ func (StubMinimalApiState) SetCode(common.Address, []byte) {
}

func (StubMinimalApiState) GetPrivacyMetadata(addr common.Address) (*state.PrivacyMetadata, error) {
panic("implement me")
if addr == arbitraryMandatoryRecipientsSimpleStorageContractAddress {
return &state.PrivacyMetadata{
CreationTxHash: arbitraryMandatoryRecipientsContractEncryptedPayloadHash,
PrivacyFlag: 2,
}, nil
}
return &state.PrivacyMetadata{
CreationTxHash: arbitrarySimpleStorageContractEncryptedPayloadHash,
PrivacyFlag: 1,
}, nil
}

func (StubMinimalApiState) GetManagedParties(addr common.Address) ([]string, error) {
Expand Down
4 changes: 4 additions & 0 deletions private/engine/constellation/constellation.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ func (g *constellation) GetParticipants(txHash common.EncryptedPayloadHash) ([]s
return nil, engine.ErrPrivateTxManagerNotSupported
}

func (g *constellation) GetMandatory(txHash common.EncryptedPayloadHash) ([]string, error) {
return nil, engine.ErrPrivateTxManagerNotSupported
}

func (g *constellation) Receive(data common.EncryptedPayloadHash) (string, []string, []byte, *engine.ExtraMetadata, error) {
if common.EmptyEncryptedPayloadHash(data) {
return "", nil, nil, nil, nil
Expand Down
4 changes: 4 additions & 0 deletions private/engine/notinuse/notInUsePrivateTxManager.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ func (ptm *PrivateTransactionManager) GetParticipants(txHash common.EncryptedPay
panic("implement me")
}

func (ptm *PrivateTransactionManager) GetMandatory(txHash common.EncryptedPayloadHash) ([]string, error) {
panic("implement me")
}

func (ptm *PrivateTransactionManager) Send(data []byte, from string, to []string, extra *engine.ExtraMetadata) (string, []string, common.EncryptedPayloadHash, error) {
return "", nil, common.EncryptedPayloadHash{}, engine.ErrPrivateTxManagerNotinUse
}
Expand Down
32 changes: 32 additions & 0 deletions private/engine/tessera/tessera.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,38 @@ func (t *tesseraPrivateTxManager) GetParticipants(txHash common.EncryptedPayload
return split, nil
}

func (t *tesseraPrivateTxManager) GetMandatory(txHash common.EncryptedPayloadHash) ([]string, error) {
requestUrl := "/transaction/" + url.PathEscape(txHash.ToBase64()) + "/mandatory"
req, err := http.NewRequest("GET", t.client.FullPath(requestUrl), nil)
if err != nil {
return nil, err
}

res, err := t.client.HttpClient.Do(req)

if res != nil {
defer res.Body.Close()
}

if err != nil {
log.Error("Failed to get mandatory recipients from tessera", "err", err)
return nil, err
}

if res.StatusCode != 200 {
return nil, fmt.Errorf("Non-200 status code: %+v", res)
}

out, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}

split := strings.Split(string(out), ",")

return split, nil
}

func (t *tesseraPrivateTxManager) Groups() ([]engine.PrivacyGroup, error) {
response := make([]engine.PrivacyGroup, 0)
if _, err := t.submitJSON("GET", "/groups/resident", nil, &response); err != nil {
Expand Down
43 changes: 42 additions & 1 deletion private/engine/tessera/tessera_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ var (
receiveRequestCaptor = make(chan *capturedRequest)
sendSignedTxRequestCaptor = make(chan *capturedRequest)
sendSignedTxOctetStreamRequestCaptor = make(chan *capturedRequest)
getMandatoryRequestCaptor = make(chan *capturedRequest)
)

type capturedRequest struct {
Expand All @@ -70,7 +71,7 @@ func Must(o interface{}, err error) interface{} {
func setup() {
mux := http.NewServeMux()
mux.HandleFunc("/send", MockSendAPIHandlerFunc)
mux.HandleFunc("/transaction/", MockReceiveAPIHandlerFunc)
mux.HandleFunc("/transaction/", MockTransactionAPIHandlerFunc)
mux.HandleFunc("/sendsignedtx", MockSendSignedTxAPIHandlerFunc)
mux.HandleFunc("/groups/resident", MockGroupsAPIHandlerFunc)

Expand Down Expand Up @@ -127,6 +128,30 @@ func MockSendAPIHandlerFunc(response http.ResponseWriter, request *http.Request)
}
}

func MockTransactionAPIHandlerFunc(response http.ResponseWriter, request *http.Request) {
if strings.HasSuffix(request.RequestURI, "/mandatory") {
MockGetMandatoryAPIHandlerFunc(response, request)
} else {
MockReceiveAPIHandlerFunc(response, request)
}
}

func MockGetMandatoryAPIHandlerFunc(response http.ResponseWriter, request *http.Request) {
actualRequest, err := url.PathUnescape(strings.TrimSuffix(strings.TrimPrefix(request.RequestURI, "/transaction/"), "/mandatory"))
if err != nil {
go func(o *capturedRequest) { getMandatoryRequestCaptor <- o }(&capturedRequest{err: err})
} else {
go func(o *capturedRequest) {
getMandatoryRequestCaptor <- o
}(&capturedRequest{request: actualRequest, header: request.Header})
if actualRequest == arbitraryNotFoundHash.ToBase64() {
response.WriteHeader(http.StatusNotFound)
} else {
response.Write([]byte(strings.Join(arbitraryMandatory, ",")))
}
}
}

func MockReceiveAPIHandlerFunc(response http.ResponseWriter, request *http.Request) {
path := string([]byte(request.RequestURI)[:strings.LastIndex(request.RequestURI, "?")])
actualRequest, err := url.PathUnescape(strings.TrimPrefix(path, "/transaction/"))
Expand Down Expand Up @@ -621,3 +646,19 @@ func TestReceive_whenCachingRawPayload(t *testing.T) {
assert.Equal(arbitraryExtra.ACMerkleRoot, actualExtra.ACMerkleRoot, "cached merkle root")
assert.Equal(arbitraryExtra.PrivacyFlag, actualExtra.PrivacyFlag, "cached privacy flag")
}

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

mandatoryRecipients, _ := testObject.GetMandatory(arbitraryHash)

assert.Equal(arbitraryMandatory, mandatoryRecipients)
}

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

_, err := testObject.GetMandatory(arbitraryNotFoundHash)

assert.Error(err, "Non-200 status code")
}
15 changes: 15 additions & 0 deletions private/mock_private.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading