Skip to content

Commit

Permalink
feat: Properly dealing with late publishing based on multiple witness…
Browse files Browse the repository at this point in the history
… timestamps

Check VC timestamp against witness timestamp.

Closes #1336

Signed-off-by: Sandra Vrtikapa <[email protected]>
  • Loading branch information
sandrask committed Jul 12, 2022
1 parent 59d14db commit 8325616
Show file tree
Hide file tree
Showing 6 changed files with 354 additions and 61 deletions.
19 changes: 16 additions & 3 deletions cmd/orb-server/startcmd/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,9 +431,14 @@ const (
maxWitnessDelayFlagShorthand = "w"
maxWitnessDelayFlagUsage = "Maximum witness response time (default 10m). " + commonEnvVarUsageText + maxWitnessDelayEnvKey

maxClockSkewFlagName = "max-clock-skew"
maxClockSkewEnvKey = "MAX_CLOCK_SKEW"
maxClockSkewFlagUsage = "Maximum clock skew (default 1m). " + commonEnvVarUsageText + maxClockSkewEnvKey

witnessStoreExpiryPeriodFlagName = "witness-store-expiry-period"
witnessStoreExpiryPeriodEnvKey = "WITNESS_STORE_EXPIRY_PERIOD"
witnessStoreExpiryPeriodFlagUsage = "Witness store expiry period has to be greater than maximum witness response time" +
witnessStoreExpiryPeriodFlagUsage = "Witness store expiry period has to be greater than " +
"maximum witness response time + clock skew time" +
"(default 12m). " + commonEnvVarUsageText + witnessStoreExpiryPeriodEnvKey

signWithLocalWitnessFlagName = "sign-with-local-witness"
Expand Down Expand Up @@ -697,6 +702,7 @@ type orbParameters struct {
discoveryDomains []string
discoveryMinimumResolvers int
maxWitnessDelay time.Duration
maxClockSkew time.Duration
witnessStoreExpiryPeriod time.Duration
syncTimeout uint64
signWithLocalWitness bool
Expand Down Expand Up @@ -966,13 +972,18 @@ func getOrbParameters(cmd *cobra.Command) (*orbParameters, error) {
return nil, err
}

maxClockSkew, err := getDuration(cmd, maxClockSkewFlagName, maxClockSkewEnvKey, defaultMaxClockSkew)
if err != nil {
return nil, err
}

witnessStoreExpiryPeriod, err := getDuration(cmd, witnessStoreExpiryPeriodFlagName, witnessStoreExpiryPeriodEnvKey, defaultWitnessStoreExpiryDelta)
if err != nil {
return nil, err
}

if witnessStoreExpiryPeriod <= maxWitnessDelay {
return nil, fmt.Errorf("witness store expiry period must me greater than maximum witness delay")
if witnessStoreExpiryPeriod <= maxWitnessDelay+maxClockSkew {
return nil, fmt.Errorf("witness store expiry period must me greater than maximum witness delay + max clock skew")
}

signWithLocalWitnessStr, err := cmdutils.GetUserSetVarFromString(cmd, signWithLocalWitnessFlagName, signWithLocalWitnessEnvKey, true)
Expand Down Expand Up @@ -1415,6 +1426,7 @@ func getOrbParameters(cmd *cobra.Command) (*orbParameters, error) {
discoveryDomains: discoveryDomains,
discoveryMinimumResolvers: discoveryMinimumResolvers,
maxWitnessDelay: maxWitnessDelay,
maxClockSkew: maxClockSkew,
witnessStoreExpiryPeriod: witnessStoreExpiryPeriod,
syncTimeout: syncTimeout,
signWithLocalWitness: signWithLocalWitness,
Expand Down Expand Up @@ -2036,6 +2048,7 @@ func createFlags(startCmd *cobra.Command) {
startCmd.Flags().StringArrayP(tlsCACertsFlagName, "", []string{}, tlsCACertsFlagUsage)
startCmd.Flags().StringP(batchWriterTimeoutFlagName, batchWriterTimeoutFlagShorthand, "", batchWriterTimeoutFlagUsage)
startCmd.Flags().StringP(maxWitnessDelayFlagName, maxWitnessDelayFlagShorthand, "", maxWitnessDelayFlagUsage)
startCmd.Flags().StringP(maxClockSkewFlagName, "", "", maxClockSkewFlagUsage)
startCmd.Flags().StringP(witnessStoreExpiryPeriodFlagName, "", "", witnessStoreExpiryPeriodFlagUsage)
startCmd.Flags().StringP(signWithLocalWitnessFlagName, signWithLocalWitnessFlagShorthand, "", signWithLocalWitnessFlagUsage)
startCmd.Flags().StringP(httpSignaturesEnabledFlagName, httpSignaturesEnabledShorthand, "", httpSignaturesEnabledUsage)
Expand Down
35 changes: 32 additions & 3 deletions cmd/orb-server/startcmd/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"testing"
"time"

"github.com/cenkalti/backoff/v4"
backoff "github.com/cenkalti/backoff/v4"
ariesmemstorage "github.com/hyperledger/aries-framework-go/component/storageutil/mem"
"github.com/hyperledger/aries-framework-go/spi/storage"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -274,6 +274,34 @@ func TestStartCmdWithMissingArg(t *testing.T) {
require.Contains(t, err.Error(), "invalid duration")
})

t.Run("test invalid max clock skew", func(t *testing.T) {
startCmd := GetStartCmd()

args := []string{
"--" + hostURLFlagName, "localhost:8247",
"--" + hostMetricsURLFlagName, "localhost:8248",
"--" + externalEndpointFlagName, "orb.example.com",
"--" + casTypeFlagName, "ipfs",
"--" + ipfsURLFlagName, "localhost:8081",
"--" + maxWitnessDelayFlagName, "10s",
"--" + maxClockSkewFlagName, "abc",
"--" + witnessStoreExpiryPeriodFlagName, "1m",
"--" + didNamespaceFlagName, "namespace", "--" + databaseTypeFlagName, databaseTypeMemOption,
"--" + kmsSecretsDatabaseTypeFlagName, databaseTypeMemOption,
"--" + anchorCredentialDomainFlagName, "domain.com",
"--" + anchorCredentialIssuerFlagName, "issuer.com",
"--" + anchorCredentialURLFlagName, "peer.com",
"--" + LogLevelFlagName, log.ParseString(log.ERROR),
}

startCmd.SetArgs(args)

err := startCmd.Execute()

require.Error(t, err)
require.Contains(t, err.Error(), "invalid duration")
})

t.Run("test invalid witness store duration - less than maximum witness delay", func(t *testing.T) {
startCmd := GetStartCmd()

Expand All @@ -298,7 +326,8 @@ func TestStartCmdWithMissingArg(t *testing.T) {
err := startCmd.Execute()

require.Error(t, err)
require.Contains(t, err.Error(), "witness store expiry period must me greater than maximum witness delay")
require.Contains(t, err.Error(),
"witness store expiry period must me greater than maximum witness delay + max clock skew")
})

t.Run("test invalid sign with local witness flag", func(t *testing.T) {
Expand Down Expand Up @@ -1788,7 +1817,7 @@ func getTestArgs(ipfsURL, casType, localCASReplicateInIPFSEnabled, databaseType,
"--" + cidVersionFlagName, "0",
"--" + batchWriterTimeoutFlagName, "700",
"--" + maxWitnessDelayFlagName, "1m",
"--" + witnessStoreExpiryPeriodFlagName, "2m",
"--" + witnessStoreExpiryPeriodFlagName, "5m",
"--" + signWithLocalWitnessFlagName, "false",
"--" + casTypeFlagName, casType,
"--" + didNamespaceFlagName, "namespace",
Expand Down
3 changes: 2 additions & 1 deletion cmd/orb-server/startcmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ const (
masterKeyURI = "local-lock://custom/master/key/"

defaultMaxWitnessDelay = 10 * time.Minute
defaultMaxClockSkew = 1 * time.Minute
defaultWitnessStoreExpiryDelta = 12 * time.Minute
defaultSyncTimeout = 1
defaulthttpSignaturesEnabled = true
Expand Down Expand Up @@ -902,7 +903,7 @@ func startOrbServices(parameters *orbParameters) error {
WitnessPolicy: witnessPolicy,
Metrics: metrics.Get(),
},
pubSub, parameters.dataURIMediaType)
pubSub, parameters.dataURIMediaType, parameters.maxClockSkew)

witness := vct.New(configclient.New(configStore), vcSigner, metrics.Get(),
vct.WithHTTPClient(httpClient),
Expand Down
88 changes: 56 additions & 32 deletions pkg/anchor/handler/proof/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ type metricsProvider interface {
}

// New creates new proof handler.
func New(providers *Providers, pubSub pubSub, dataURIMediaType datauri.MediaType) *WitnessProofHandler {
func New(providers *Providers, pubSub pubSub, dataURIMediaType datauri.MediaType,
maxClockSkew time.Duration) *WitnessProofHandler {
return &WitnessProofHandler{
Providers: providers,
publisher: vcpubsub.NewPublisher(pubSub),
dataURIMediaType: dataURIMediaType,
maxClockSkew: maxClockSkew,
}
}

Expand All @@ -69,6 +71,7 @@ type WitnessProofHandler struct {
*Providers
publisher anchorLinkPublisher
dataURIMediaType vocab.MediaType
maxClockSkew time.Duration
}

type witnessStore interface {
Expand All @@ -94,16 +97,48 @@ type witnessPolicy interface {
}

// HandleProof handles proof.
func (h *WitnessProofHandler) HandleProof(witness *url.URL, anchor string, endTime time.Time, proof []byte) error { //nolint:lll
func (h *WitnessProofHandler) HandleProof(witness *url.URL, anchor string, endTime time.Time, proof []byte) error { //nolint:lll,funlen,gocyclo,cyclop
logger.Debugf("received proof for anchor [%s] from witness[%s], proof: %s",
anchor, witness.String(), string(proof))

serverTime := time.Now().Unix()
var witnessProof vct.Proof

err := json.Unmarshal(proof, &witnessProof)
if err != nil {
return fmt.Errorf("failed to unmarshal incoming witness proof for anchor [%s]: %w", anchor, err)
}

anchorLink, err := h.AnchorLinkStore.Get(anchor)
if err != nil {
return fmt.Errorf("failed to retrieve anchor link [%s]: %w", anchor, err)
}

if endTime.Unix() < serverTime {
// proof came after expiry time so nothing to do here
vc, err := util.VerifiableCredentialFromAnchorLink(anchorLink,
verifiable.WithDisabledProofCheck(),
verifiable.WithJSONLDDocumentLoader(h.DocLoader),
)
if err != nil {
return fmt.Errorf("failed get verifiable credential from anchor: %w", err)
}

vcIssuedTime := vc.Issued.Time

proofCreatedTime, err := getCreatedTime(witnessProof)
if err != nil {
return fmt.Errorf("failed to get create time from witness[%s] proof for anchor[%s] : %w",
witness.String(), anchor, err)
}

endTimeForProof := endTime.Add(h.maxClockSkew)
startTimeForProof := vcIssuedTime.Add(-1 * h.maxClockSkew)

if proofCreatedTime.Before(startTimeForProof) || proofCreatedTime.After(endTimeForProof) {
// proof created time is after expiry time or before create time so nothing to do here
// clean up process for witness store and Sidetree batch files will have to be initiated differently
// since we can have scenario that proof never shows up
logger.Infof("proof created time[%s] for anchor[%s] from witness[%s] "+
"is either too early or too late.", proofCreatedTime, anchor, witness.String())

return nil
}

Expand All @@ -120,32 +155,12 @@ func (h *WitnessProofHandler) HandleProof(witness *url.URL, anchor string, endTi
return nil
}

var witnessProof vct.Proof

err = json.Unmarshal(proof, &witnessProof)
if err != nil {
return fmt.Errorf("failed to unmarshal incoming witness proof for anchor [%s]: %w", anchor, err)
}

anchorLink, err := h.AnchorLinkStore.Get(anchor)
if err != nil {
return fmt.Errorf("failed to retrieve anchor link [%s]: %w", anchor, err)
}

err = h.WitnessStore.AddProof(anchor, witness, proof)
if err != nil {
return fmt.Errorf("failed to add witness[%s] proof for anchor [%s]: %w",
witness.String(), anchor, err)
}

vc, err := util.VerifiableCredentialFromAnchorLink(anchorLink,
verifiable.WithDisabledProofCheck(),
verifiable.WithJSONLDDocumentLoader(h.DocLoader),
)
if err != nil {
return fmt.Errorf("failed get verifiable credential from anchor: %w", err)
}

err = h.setupMonitoring(witnessProof, vc, endTime)
if err != nil {
return fmt.Errorf("failed to setup monitoring for anchor [%s]: %w", anchor, err)
Expand All @@ -155,14 +170,9 @@ func (h *WitnessProofHandler) HandleProof(witness *url.URL, anchor string, endTi
}

func (h *WitnessProofHandler) setupMonitoring(wp vct.Proof, vc *verifiable.Credential, endTime time.Time) error {
var created string
if createdVal, ok := wp.Proof["created"].(string); ok {
created = createdVal
}

createdTime, err := time.Parse(time.RFC3339, created)
createdTime, err := getCreatedTime(wp)
if err != nil {
return fmt.Errorf("parse created: %w", err)
return err
}

var domain string
Expand All @@ -173,6 +183,20 @@ func (h *WitnessProofHandler) setupMonitoring(wp vct.Proof, vc *verifiable.Crede
return h.MonitoringSvc.Watch(vc, endTime, domain, createdTime)
}

func getCreatedTime(wp vct.Proof) (time.Time, error) {
var created string
if createdVal, ok := wp.Proof["created"].(string); ok {
created = createdVal
}

createdTime, err := time.Parse(time.RFC3339, created)
if err != nil {
return time.Time{}, fmt.Errorf("parse created: %w", err)
}

return createdTime, nil
}

func (h *WitnessProofHandler) handleWitnessPolicy(anchorLink *linkset.Link, vc *verifiable.Credential) error { //nolint:funlen,gocyclo,cyclop,lll
anchorID := anchorLink.Anchor().String()

Expand Down
Loading

0 comments on commit 8325616

Please sign in to comment.