Skip to content

Commit

Permalink
chore: support multiple multihash algorithms in protocol
Browse files Browse the repository at this point in the history
Closes #522

Signed-off-by: Sandra Vrtikapa <[email protected]>
  • Loading branch information
sandrask committed Dec 11, 2020
1 parent 188eb39 commit da8dfdd
Show file tree
Hide file tree
Showing 18 changed files with 84 additions and 71 deletions.
4 changes: 2 additions & 2 deletions pkg/api/protocol/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ type Protocol struct {
// GenesisTime is inclusive starting logical blockchain time that this protocol applies to.
// (e.g. block number in a blockchain)
GenesisTime uint64 `json:"genesisTime"`
// MultihashAlgorithm is multihash algorithm code.
MultihashAlgorithm uint `json:"multihashAlgorithm"`
// MultihashAlgorithms are supported multihash algorithm codes
MultihashAlgorithms []uint `json:"multihashAlgorithms"`
// MaxOperationCount defines maximum number of operations per batch.
MaxOperationCount uint `json:"maxOperationCount"`
// MaxOperationSize is maximum operation size in bytes (used to reject operations before parsing them)
Expand Down
12 changes: 9 additions & 3 deletions pkg/hashing/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,20 @@ func IsSupportedMultihash(encodedMultihash string) bool {
return multihash.ValidCode(code)
}

// IsComputedUsingMultihashAlgorithm checks to see if the given encoded hash has been hashed using multihash code.
func IsComputedUsingMultihashAlgorithm(encodedMultihash string, code uint64) bool {
// IsComputedUsingMultihashAlgorithms checks to see if the given encoded hash has been hashed using one of supplied code.
func IsComputedUsingMultihashAlgorithms(encodedMultihash string, codes []uint) bool {
mhCode, err := GetMultihashCode(encodedMultihash)
if err != nil {
return false
}

return mhCode == code
for _, supported := range codes {
if mhCode == uint64(supported) {
return true
}
}

return false
}

// GetMultihashCode returns multihash code from encoded multihash.
Expand Down
6 changes: 3 additions & 3 deletions pkg/hashing/hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ func TestIsComputedUsingHashAlgorithm(t *testing.T) {
require.NotNil(t, hash)

key := encoder.EncodeToString(hash)
ok := IsComputedUsingMultihashAlgorithm(key, sha2_256)
ok := IsComputedUsingMultihashAlgorithms(key, []uint{sha2_256})
require.True(t, ok)

// use random code to fail
ok = IsComputedUsingMultihashAlgorithm(key, 55)
ok = IsComputedUsingMultihashAlgorithms(key, []uint{55})
require.False(t, ok)

ok = IsComputedUsingMultihashAlgorithm("invalid", sha2_256)
ok = IsComputedUsingMultihashAlgorithms("invalid", []uint{sha2_256})
require.False(t, ok)
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/mocks/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func GetDefaultProtocolParameters() protocol.Protocol {
//nolint:gomnd
return protocol.Protocol{
GenesisTime: 0,
MultihashAlgorithm: sha2_256,
MultihashAlgorithms: []uint{sha2_256},
MaxOperationCount: 2,
MaxOperationSize: MaxOperationByteSize,
MaxOperationHashLength: 100,
Expand Down
26 changes: 13 additions & 13 deletions pkg/processor/processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func TestUpdateDocument(t *testing.T) {
pubJWK, err := pubkey.GetPublicKeyJWK(&updateKey.PublicKey)
require.NoError(t, err)

rv, err := commitment.GetRevealValue(pubJWK, getProtocol(1).MultihashAlgorithm)
rv, err := commitment.GetRevealValue(pubJWK, getProtocol(1).MultihashAlgorithms[0])
require.NoError(t, err)

// protocol value for hashing algorithm changed at block 100
Expand Down Expand Up @@ -210,7 +210,7 @@ func TestUpdateDocument(t *testing.T) {
require.NoError(t, err)

// previous operation commit value was calculated with protocol value at block 50
rv, err := commitment.GetRevealValue(pubJWK, getProtocol(50).MultihashAlgorithm)
rv, err := commitment.GetRevealValue(pubJWK, getProtocol(50).MultihashAlgorithms[0])
require.NoError(t, err)

// protocol value for hashing algorithm changed at block 100
Expand Down Expand Up @@ -478,7 +478,7 @@ func TestRecover(t *testing.T) {
pubJWK, err := pubkey.GetPublicKeyJWK(&recoveryKey.PublicKey)
require.NoError(t, err)

rv, err := commitment.GetRevealValue(pubJWK, getProtocol(1).MultihashAlgorithm)
rv, err := commitment.GetRevealValue(pubJWK, getProtocol(1).MultihashAlgorithms[0])
require.NoError(t, err)

// hashing algorithm changed at block 100
Expand Down Expand Up @@ -538,7 +538,7 @@ func TestRecover(t *testing.T) {
pubJWK, err := pubkey.GetPublicKeyJWK(&nextRecoveryKey.PublicKey)
require.NoError(t, err)

rv, err := commitment.GetRevealValue(pubJWK, getProtocol(50).MultihashAlgorithm)
rv, err := commitment.GetRevealValue(pubJWK, getProtocol(50).MultihashAlgorithms[0])
require.NoError(t, err)

// hashing algorithm changed at block 100
Expand Down Expand Up @@ -906,7 +906,7 @@ func getUpdateOperationWithSigner(s client.Signer, privateKey *ecdsa.PrivateKey,
Patches: []patch.Patch{jsonPatch},
}

deltaHash, err := hashing.CalculateModelMultihash(delta, getProtocol(blockNumber).MultihashAlgorithm)
deltaHash, err := hashing.CalculateModelMultihash(delta, getProtocol(blockNumber).MultihashAlgorithms[0])
if err != nil {
return nil, nil, err
}
Expand All @@ -926,7 +926,7 @@ func getUpdateOperationWithSigner(s client.Signer, privateKey *ecdsa.PrivateKey,
return nil, nil, err
}

rv, err := commitment.GetRevealValue(updatePubKey, getProtocol(blockNumber).MultihashAlgorithm)
rv, err := commitment.GetRevealValue(updatePubKey, getProtocol(blockNumber).MultihashAlgorithms[0])
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -955,7 +955,7 @@ func generateKeyAndCommitment(p protocol.Protocol) (*ecdsa.PrivateKey, string, e
return nil, "", err
}

c, err := commitment.GetCommitment(pubKey, p.MultihashAlgorithm)
c, err := commitment.GetCommitment(pubKey, p.MultihashAlgorithms[0])
if err != nil {
return nil, "", err
}
Expand Down Expand Up @@ -1046,7 +1046,7 @@ func getRecoverOperationWithSigner(signer client.Signer, recoveryKey, updateKey
}

func getRecoverRequest(signer client.Signer, deltaModel *model.DeltaModel, signedDataModel *model.RecoverSignedDataModel, blockNum uint64) (*model.RecoverRequest, error) {
deltaHash, err := hashing.CalculateModelMultihash(deltaModel, getProtocol(blockNum).MultihashAlgorithm)
deltaHash, err := hashing.CalculateModelMultihash(deltaModel, getProtocol(blockNum).MultihashAlgorithms[0])
if err != nil {
return nil, err
}
Expand All @@ -1058,7 +1058,7 @@ func getRecoverRequest(signer client.Signer, deltaModel *model.DeltaModel, signe
return nil, err
}

rv, err := commitment.GetRevealValue(signedDataModel.RecoveryKey, getProtocol(blockNum).MultihashAlgorithm)
rv, err := commitment.GetRevealValue(signedDataModel.RecoveryKey, getProtocol(blockNum).MultihashAlgorithms[0])
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1087,7 +1087,7 @@ func getDefaultRecoverRequest(signer client.Signer, recoveryKey, updateKey *ecds
return nil, nil, err
}

deltaHash, err := hashing.CalculateModelMultihash(delta, p.MultihashAlgorithm)
deltaHash, err := hashing.CalculateModelMultihash(delta, p.MultihashAlgorithms[0])
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -1253,7 +1253,7 @@ func getCommitment(key *ecdsa.PrivateKey, p protocol.Protocol) (string, error) {
return "", err
}

return commitment.GetCommitment(pubKey, p.MultihashAlgorithm)
return commitment.GetCommitment(pubKey, p.MultihashAlgorithms[0])
}

func getSuffixData(privateKey *ecdsa.PrivateKey, delta *model.DeltaModel, p protocol.Protocol) (*model.SuffixDataModel, error) {
Expand All @@ -1262,7 +1262,7 @@ func getSuffixData(privateKey *ecdsa.PrivateKey, delta *model.DeltaModel, p prot
return nil, err
}

deltaHash, err := hashing.CalculateModelMultihash(delta, p.MultihashAlgorithm)
deltaHash, err := hashing.CalculateModelMultihash(delta, p.MultihashAlgorithms[0])
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1319,7 +1319,7 @@ func newMockProtocolClient() *mocks.MockProtocolClient {
//nolint:gomnd
latest := protocol.Protocol{
GenesisTime: 100,
MultihashAlgorithm: sha2_512,
MultihashAlgorithms: []uint{sha2_512, sha2_256},
MaxOperationCount: 2,
MaxOperationSize: mocks.MaxOperationByteSize,
MaxOperationHashLength: 100,
Expand Down
4 changes: 2 additions & 2 deletions pkg/versions/0_1/client/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ func validateCreateRequest(info *CreateRequestInfo) error {
return fmt.Errorf("multihash[%d] not supported", info.MultihashCode)
}

if !hashing.IsComputedUsingMultihashAlgorithm(info.RecoveryCommitment, uint64(info.MultihashCode)) {
if !hashing.IsComputedUsingMultihashAlgorithms(info.RecoveryCommitment, []uint{info.MultihashCode}) {
return errors.New("next recovery commitment is not computed with the specified hash algorithm")
}

if !hashing.IsComputedUsingMultihashAlgorithm(info.UpdateCommitment, uint64(info.MultihashCode)) {
if !hashing.IsComputedUsingMultihashAlgorithms(info.UpdateCommitment, []uint{info.MultihashCode}) {
return errors.New("next update commitment is not computed with the specified hash algorithm")
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/versions/0_1/operationapplier/operationapplier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const (
var (
p = protocol.Protocol{
GenesisTime: 0,
MultihashAlgorithm: sha2_256,
MultihashAlgorithms: []uint{sha2_256},
MaxOperationCount: 2,
MaxOperationSize: 2000,
MaxOperationHashLength: 100,
Expand Down
14 changes: 7 additions & 7 deletions pkg/versions/0_1/operationparser/commitment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func TestParser_GetRevealValue(t *testing.T) {
pubJWK, err := pubkey.GetPublicKeyJWK(&recoveryKey.PublicKey)
require.NoError(t, err)

expected, err := commitment.GetRevealValue(pubJWK, parser.Protocol.MultihashAlgorithm)
expected, err := commitment.GetRevealValue(pubJWK, parser.Protocol.MultihashAlgorithms[0])
require.NoError(t, err)

require.Equal(t, rv, expected)
Expand All @@ -143,7 +143,7 @@ func TestParser_GetRevealValue(t *testing.T) {
pubJWK, err := pubkey.GetPublicKeyJWK(&recoveryKey.PublicKey)
require.NoError(t, err)

expected, err := commitment.GetRevealValue(pubJWK, parser.Protocol.MultihashAlgorithm)
expected, err := commitment.GetRevealValue(pubJWK, parser.Protocol.MultihashAlgorithms[0])
require.NoError(t, err)

require.Equal(t, rv, expected)
Expand All @@ -160,7 +160,7 @@ func TestParser_GetRevealValue(t *testing.T) {
pubJWK, err := pubkey.GetPublicKeyJWK(&updateKey.PublicKey)
require.NoError(t, err)

expected, err := commitment.GetRevealValue(pubJWK, parser.Protocol.MultihashAlgorithm)
expected, err := commitment.GetRevealValue(pubJWK, parser.Protocol.MultihashAlgorithms[0])
require.NoError(t, err)

require.Equal(t, rv, expected)
Expand Down Expand Up @@ -206,7 +206,7 @@ func generateRecoverRequest(recoveryKey *ecdsa.PrivateKey, recoveryCommitment st
RecoveryCommitment: recoveryCommitment,
UpdateCommitment: updateCommitment, // not evaluated in operation getting commitment/reveal value
RecoveryKey: jwk,
MultihashCode: p.MultihashAlgorithm,
MultihashCode: p.MultihashAlgorithms[0],
Signer: ecsigner.New(recoveryKey, "ES256", ""),
RevealValue: rv,
}
Expand All @@ -219,7 +219,7 @@ func generateCreateRequest(recoveryCommitment, updateCommitment string, p protoc
OpaqueDocument: `{"test":"value"}`,
RecoveryCommitment: recoveryCommitment,
UpdateCommitment: updateCommitment,
MultihashCode: p.MultihashAlgorithm,
MultihashCode: p.MultihashAlgorithms[0],
}

return client.NewCreateRequest(info)
Expand Down Expand Up @@ -268,7 +268,7 @@ func generateUpdateRequest(updateKey *ecdsa.PrivateKey, updateCommitment string,
UpdateCommitment: updateCommitment,
UpdateKey: jwk,
Patches: []patch.Patch{testPatch},
MultihashCode: p.MultihashAlgorithm,
MultihashCode: p.MultihashAlgorithms[0],
RevealValue: rv,
}

Expand All @@ -286,7 +286,7 @@ func generateKeyAndCommitment(p protocol.Protocol) (*ecdsa.PrivateKey, string, e
return nil, "", err
}

c, err := commitment.GetCommitment(pubKey, p.MultihashAlgorithm)
c, err := commitment.GetCommitment(pubKey, p.MultihashAlgorithms[0])
if err != nil {
return nil, "", err
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/versions/0_1/operationparser/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ func (p *Parser) ParseCreateOperation(request []byte, batch bool) (*model.Operat
}
}

uniqueSuffix, err := hashing.CalculateModelMultihash(schema.SuffixData, p.MultihashAlgorithm)
// TODO: Decide which one to use for generating id; default to first one for now
uniqueSuffix, err := hashing.CalculateModelMultihash(schema.SuffixData, p.MultihashAlgorithms[0])
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -111,8 +112,8 @@ func (p *Parser) validateMultihash(mh, alias string) error {
return fmt.Errorf("%s length[%d] exceeds maximum hash length[%d]", alias, len(mh), p.MaxOperationHashLength)
}

if !hashing.IsComputedUsingMultihashAlgorithm(mh, uint64(p.MultihashAlgorithm)) {
return fmt.Errorf("%s is not computed with the required hash algorithm: %d", alias, p.MultihashAlgorithm)
if !hashing.IsComputedUsingMultihashAlgorithms(mh, p.MultihashAlgorithms) {
return fmt.Errorf("%s is not computed with the required hash algorithms: %d", alias, p.MultihashAlgorithms)
}

return nil
Expand Down
20 changes: 10 additions & 10 deletions pkg/versions/0_1/operationparser/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func TestParseCreateOperation(t *testing.T) {
p := protocol.Protocol{
MaxOperationHashLength: 100,
MaxDeltaSize: maxDeltaSize,
MultihashAlgorithm: sha2_256,
MultihashAlgorithms: []uint{sha2_256},
Patches: []string{"replace", "add-public-keys", "remove-public-keys", "add-services", "remove-services", "ietf-json-patch"},
}

Expand Down Expand Up @@ -81,7 +81,7 @@ func TestParseCreateOperation(t *testing.T) {

op, err := parser.ParseCreateOperation(request, true)
require.Error(t, err)
require.Contains(t, err.Error(), "recovery commitment is not computed with the required hash algorithm: 18")
require.Contains(t, err.Error(), "recovery commitment is not computed with the required hash algorithms: [18]")
require.Nil(t, op)
})
t.Run("missing delta", func(t *testing.T) {
Expand Down Expand Up @@ -162,7 +162,7 @@ func TestParseCreateOperation(t *testing.T) {
func TestValidateSuffixData(t *testing.T) {
p := protocol.Protocol{
MaxOperationHashLength: maxHashLength,
MultihashAlgorithm: sha2_256,
MultihashAlgorithms: []uint{sha2_256},
}

parser := New(p)
Expand All @@ -174,7 +174,7 @@ func TestValidateSuffixData(t *testing.T) {
suffixData.DeltaHash = ""
err = parser.ValidateSuffixData(suffixData)
require.Error(t, err)
require.Contains(t, err.Error(), "delta hash is not computed with the required hash algorithm: 18")
require.Contains(t, err.Error(), "delta hash is not computed with the required hash algorithms: [18]")
})
t.Run("invalid next recovery commitment hash", func(t *testing.T) {
suffixData, err := getSuffixData()
Expand All @@ -183,12 +183,12 @@ func TestValidateSuffixData(t *testing.T) {
suffixData.RecoveryCommitment = ""
err = parser.ValidateSuffixData(suffixData)
require.Error(t, err)
require.Contains(t, err.Error(), "recovery commitment is not computed with the required hash algorithm: 18")
require.Contains(t, err.Error(), "recovery commitment is not computed with the required hash algorithms: [18]")
})
t.Run("recovery commitment exceeds maximum hash length", func(t *testing.T) {
lowHashLength := protocol.Protocol{
MaxOperationHashLength: 10,
MultihashAlgorithm: sha2_256,
MultihashAlgorithms: []uint{sha2_256},
}

suffixData, err := getSuffixData()
Expand All @@ -206,7 +206,7 @@ func TestValidateDelta(t *testing.T) {
p := protocol.Protocol{
MaxOperationHashLength: maxHashLength,
MaxDeltaSize: maxDeltaSize,
MultihashAlgorithm: sha2_256,
MultihashAlgorithms: []uint{sha2_256},
Patches: patches,
}

Expand All @@ -224,7 +224,7 @@ func TestValidateDelta(t *testing.T) {
parserWithLowMaxDeltaSize := New(protocol.Protocol{
MaxOperationHashLength: maxHashLength,
MaxDeltaSize: 50,
MultihashAlgorithm: sha2_256,
MultihashAlgorithms: []uint{sha2_256},
Patches: patches,
})

Expand All @@ -244,14 +244,14 @@ func TestValidateDelta(t *testing.T) {
err = parser.ValidateDelta(delta)
require.Error(t, err)
require.Contains(t, err.Error(),
"update commitment is not computed with the required hash algorithm: 18")
"update commitment is not computed with the required hash algorithms: [18]")
})

t.Run("update commitment exceeds maximum hash length", func(t *testing.T) {
lowMaxHashLength := protocol.Protocol{
MaxOperationHashLength: 10,
MaxDeltaSize: 50,
MultihashAlgorithm: sha2_256,
MultihashAlgorithms: []uint{sha2_256},
Patches: patches,
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/versions/0_1/operationparser/deactivate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const sha2_256 = 18
func TestParseDeactivateOperation(t *testing.T) {
p := protocol.Protocol{
MaxProofSize: maxProofSize,
MultihashAlgorithm: sha2_256,
MultihashAlgorithms: []uint{sha2_256},
MaxOperationHashLength: maxHashLength,
SignatureAlgorithms: []string{"alg"},
KeyAlgorithms: []string{"crv"},
Expand Down Expand Up @@ -116,7 +116,7 @@ func TestParseDeactivateOperation(t *testing.T) {
t.Run("error - key algorithm not supported", func(t *testing.T) {
p := protocol.Protocol{
MaxProofSize: maxProofSize,
MultihashAlgorithm: sha2_256,
MultihashAlgorithms: []uint{sha2_256},
MaxOperationHashLength: maxHashLength,
SignatureAlgorithms: []string{"alg"},
KeyAlgorithms: []string{"other"},
Expand Down
Loading

0 comments on commit da8dfdd

Please sign in to comment.