diff --git a/go.mod b/go.mod index 3522742..b64ed03 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,59 @@ module github.com/datatrails/go-datatrails-merklelog go 1.21 -require github.com/stretchr/testify v1.8.4 +require ( + github.com/datatrails/go-datatrails-common v0.12.2 + github.com/google/uuid v1.6.0 + github.com/stretchr/testify v1.8.4 +) require ( + github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1 // indirect + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest v0.11.29 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect + github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect + github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect + github.com/Azure/go-autorest/logger v0.2.1 // indirect + github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/KimMachineGun/automemlimit v0.2.6 // indirect + github.com/cilium/ebpf v0.9.1 // indirect + github.com/containerd/cgroups/v3 v3.0.1 // indirect + github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dimchansky/utfbom v1.1.1 // indirect + github.com/docker/go-units v0.4.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/godbus/dbus/v5 v5.0.4 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/opencontainers/runtime-spec v1.0.2 // indirect + github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect + github.com/opentracing-contrib/go-stdlib v1.0.0 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 // indirect + github.com/openzipkin/zipkin-go v0.4.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/sirupsen/logrus v1.8.1 // indirect + go.uber.org/automaxprocs v1.5.3 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.25.0 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/grpc v1.57.1 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index fa4b6e6..c5eca5b 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,281 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= +github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0 h1:Yoicul8bnVdQrhDMTHxdEckRGX01XvwXDHUT9zYZ3k0= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1 h1:QSdcrd/UFJv6Bp/CfoVf2SrENpFn9P6Yh8yb+xNhYMM= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1/go.mod h1:eZ4g6GUvXiGulfIbbhh1Xr4XwUYaYaWMqzGD/284wCA= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= +github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= +github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= +github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/adal v0.9.22 h1:/GblQdIudfEM3AWWZ0mrYJQSd7JS4S/Mbzh6F0ov0Xc= +github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= +github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 h1:WVsrXCnHlDDX8ls+tootqRE87/hL9S/g4ewig9RsD/c= +github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/KimMachineGun/automemlimit v0.2.6 h1:tQFriVTcIteUkV5EgU9iz03eDY36T8JU5RAjP2r6Kt0= +github.com/KimMachineGun/automemlimit v0.2.6/go.mod h1:pJhTW/nWJMj6SnWSU2TEKSlCaM+1N5Mej+IfS/5/Ol0= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4= +github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/containerd/cgroups/v3 v3.0.1 h1:4hfGvu8rfGIwVIDd+nLzn/B9ZXx4BcCjzt5ToenJRaE= +github.com/containerd/cgroups/v3 v3.0.1/go.mod h1:/vtwk1VXrtoa5AaZLkypuOJgA/6DyPMZHJPGQNtlHnw= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/datatrails/go-datatrails-common v0.12.2 h1:OwxvJ5bD9wU3O0y/F4uxDDNvuroFzpTcbACevuRMqME= +github.com/datatrails/go-datatrails-common v0.12.2/go.mod h1:uiojIjIRdQya//pQnDp6vMgLDBPmtYH07tmMTDGKfD8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= +github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= +github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1 h1:LSsiG61v9IzzxMkqEr6nrix4miJI62xlRjwT7BYD2SM= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.17.1/go.mod h1:Hbb13e3/WtqQ8U5hLGkek9gJvBLasHuPFI0UEGfnQ10= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w= +github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 h1:uhcF5Jd7rP9DVEL10Siffyepr6SvlKbUsjH5JpNCRi8= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0/go.mod h1:+oCZ5GXXr7KPI/DNOQORPTq5AWHfALJj9c72b0+YsEY= +github.com/openzipkin/zipkin-go v0.4.2 h1:zjqfqHjUpPmB3c1GlCvvgsM1G4LkvqQbBDueDOCg/jA= +github.com/openzipkin/zipkin-go v0.4.2/go.mod h1:ZeVkFjuuBiSy13y8vpSDCjMi9GoI3hPpCJSBx/EYFhY= +github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI= +github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= +go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= +google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/mmrblobs/blobread.go b/mmrblobs/blobread.go new file mode 100644 index 0000000..ac900fb --- /dev/null +++ b/mmrblobs/blobread.go @@ -0,0 +1,41 @@ +package mmrblobs + +import ( + "context" + "errors" + "io" + + "github.com/datatrails/go-datatrails-common/azblob" +) + +// BlobRead reads the blob of the given store. +func BlobRead( + ctx context.Context, blobPath string, store massifReader, + opts ...azblob.Option) (*azblob.ReaderResponse, []byte, error) { + + rr, err := store.Reader(ctx, blobPath, opts...) + if err != nil { + return nil, nil, err + } + + data := make([]byte, rr.ContentLength) + read := int64(0) + for read < rr.ContentLength { + n, err := rr.Reader.Read(data[read:]) + if err != nil && !errors.Is(err, io.EOF) { + return nil, nil, err + } + read += int64(n) + } + + // The reader is now definitely exhausted for the purpose it was created. To + // avoid odd effects, or accidental misuse we nill it out. And we do so regardless of error. + + rr.Reader = nil // The caller has no use for this + + // If we read less. truncate the buffer + if read < int64(len(data)) { + data = data[0:read] + } + return rr, data, nil +} diff --git a/mmrblobs/doc.go b/mmrblobs/doc.go new file mode 100644 index 0000000..861138e --- /dev/null +++ b/mmrblobs/doc.go @@ -0,0 +1,7 @@ +package mmrblobs + +/* + +Module for reading mmr blob (massif) from blob storage. + +*/ diff --git a/mmrblobs/errors.go b/mmrblobs/errors.go new file mode 100644 index 0000000..ab2c0aa --- /dev/null +++ b/mmrblobs/errors.go @@ -0,0 +1,7 @@ +package mmrblobs + +import "errors" + +var ( + ErrNotleaf = errors.New("mmr node not a leaf") +) diff --git a/mmrblobs/indexentry.go b/mmrblobs/indexentry.go new file mode 100644 index 0000000..c7dde8c --- /dev/null +++ b/mmrblobs/indexentry.go @@ -0,0 +1,72 @@ +package mmrblobs + +import ( + "encoding/binary" + "errors" + + "github.com/google/uuid" +) + +const ( + IndexEntryBytes = 32 * 2 + KeyBitSizeLogBase2 = 8 + KeyByteSizeLogBase2 = 5 + + EventIDFirst = 0 + EventIDEnd = EventIDFirst + 16 + SnowflakeIdFirst = 24 + SnowflakeIdEnd = SnowflakeIdFirst + 8 + AssetIDFirst = SnowflakeIdEnd + AssetIDEnd = AssetIDFirst + 16 +) + +var ( + ErrIndexEntryBadSize = errors.New("log index size invalid") +) + +// EmptyIndexEntry is a convenience method for unit tests that don't require a valid index entry +func EmptyIndexEntry() []byte { + return make([]byte, IndexEntryBytes) +} + +func SetIndexSnowflakeID( + data []byte, offset uint64, + snowflakeId uint64, +) { + binary.BigEndian.PutUint64(data[offset+SnowflakeIdFirst:offset+SnowflakeIdEnd], snowflakeId) +} + +func GetIndexSnowflakeID( + data []byte, offset uint64, +) uint64 { + return binary.BigEndian.Uint64(data[offset+SnowflakeIdFirst : offset+SnowflakeIdEnd]) +} + +// NewIndexEntry creates an index entry directly from the required components +func NewIndexEntry( + assetId uuid.UUID, eventId uuid.UUID, snowflakeId uint64, +) []byte { + index := [IndexEntryBytes]byte{} + + SetIndexEntry(index[:], 0, assetId, eventId, snowflakeId) + return index[:] +} + +// SetIndexEntry populates the mmr blob index entry at the provided data offset +// +// | 0 - 127 | 128 - 185| 184 - 191 | 192 - 255 | +// | event uuid| reserved | reserved (epoch) | snowflakeid| +// | 0 - 15 | 16 - 22| 23 | 24 - 31| +// | 16 | 7 | 1 | 8 | +// | asset uuid| reserved | +// | 256 - 384| 384 - - 512 | +// | 16 | 16 | +func SetIndexEntry( + data []byte, offset uint64, + assetId uuid.UUID, eventId uuid.UUID, snowflakeId uint64, +) { + copy(data[offset+EventIDFirst:offset+EventIDEnd], eventId[:]) + copy(data[offset+AssetIDFirst:offset+AssetIDEnd], assetId[:]) + + binary.BigEndian.PutUint64(data[offset+SnowflakeIdFirst:offset+SnowflakeIdEnd], snowflakeId) +} diff --git a/mmrblobs/indexheader.go b/mmrblobs/indexheader.go new file mode 100644 index 0000000..f99a36e --- /dev/null +++ b/mmrblobs/indexheader.go @@ -0,0 +1,29 @@ +package mmrblobs + +const ( +// . | 0 | | 21 - 22 | 23 26|27 27| 28 - 31 | + +) + +// IndexHeader exists to keep track of the number of leaves represented by the +// mmr data. +// +// Background: +// +// By keeping the index and the log together, we guarantee mutual consistency - +// provided the log and the idex values are correctly calculated, a single write +// commits the change back to the blob store. +// +// Because the data is combined, we can't use file size as a proxy for the +// membership count. +// +// Regardless of whether we pre-allocate the index data or whether we accumulate +// it as we do the mmr, we need to know how many leaves are in the index. An +// algorithm to derive a leaf index form an MMR position exists, it is sub +// linear but a bit fiddly to get right. +// +// At least for now, we are going to explicitly track the count of leaves in a +// counter value in the blob. +type IndexHeader struct { + Index uint64 +} diff --git a/mmrblobs/logentry.go b/mmrblobs/logentry.go new file mode 100644 index 0000000..7f77504 --- /dev/null +++ b/mmrblobs/logentry.go @@ -0,0 +1,60 @@ +package mmrblobs + +import "errors" + +// To enable exclusion proofs and history independent proof of completion we +// assemble the log as an array of KEY, VALUE. Each is both 32 bytes. + +const ( + ValueBits = 128 + ValueBytes = 32 + IndexHeaderBytes = 32 + LogEntryBytes = 32 + EntryByteSizeLogBase2 = 5 + ValueBitSizeLogBase2 = 8 + ValueByteSizeLogBase2 = 5 +) + +var ( + ErrLogEntryToSmall = errors.New("to few bytes to represent a valid log entry") + ErrLogValueToSmall = errors.New("to few bytes to represent a valid log value") + ErrLogValueBadSize = errors.New("log value size invalid") +) + +func IndexFromBlobSize(size int) uint64 { + if size == 0 { + return 0 + } + return uint64(size>>EntryByteSizeLogBase2) - 1 +} + +type LogEntry struct { + Data []byte +} + +// IndexedValue returns the value bytes from log data corresponding to entry +// index i. No range checks are performed, out of range will panic +func IndexedLogValue(logData []byte, i uint64) []byte { + return logData[i*LogEntryBytes : i*LogEntryBytes+ValueBytes] +} + +func (le LogEntry) Value() []byte { + return le.Data[32:64] +} + +func (le LogEntry) Entry() []byte { + return le.Data +} + +func (le *LogEntry) CopyBytes(b []byte) int { + le.Data = make([]byte, ValueBytes) + return copy(le.Data, b) +} + +func (le *LogEntry) SetBytes(b []byte) error { + if len(b) < (1 << EntryByteSizeLogBase2) { + return ErrLogEntryToSmall + } + le.Data = b + return nil +} diff --git a/mmrblobs/massifcontext.go b/mmrblobs/massifcontext.go new file mode 100644 index 0000000..7a793e6 --- /dev/null +++ b/mmrblobs/massifcontext.go @@ -0,0 +1,227 @@ +package mmrblobs + +import ( + "errors" + "fmt" + "time" +) + +var ( + ErrGetIndexUnavailable = errors.New("requested mmr index not available") + ErrAncestorStackInvalid = errors.New("the ancestor stack is invalid due to bad header information") +) + +// MassifContext enables retrieving the tenants log from blob storage. +// +// +// It is constructed entirely from data held in the massif blob and the blob +// imediately prior to it. Given the blob itself and only the 'tail nodes' from +// the preceding blob, it is possible to generate proofs without knowlege of any +// further blobs. +// +// Massif blobs are defined by the _fixed_ number of _leaves_ they contain. We +// require that count to be a power of 2 and > 1. Given that, the number of +// nodes in a massif is just: n + n - 1. This follows from the binary nature of +// the tree. +// +// For example, with n leaves = 4 we get: 4 + 3 = 7 +// +// This is the corresponding 'position' tree, with indication of how the MMR is +// 'chunked' into sub mountain ranges which we call 'massifs' +// +// 3 \ 15 massif 1 \ +// \/ \ \ +// massif 0 /\ \ | 'alpine zone' is above the massif tree line +// / \ \ | +// 2 ..... 7.....|....14........|...... 22 ..... Masif Root Index identifies the massif root +// / \ | / \ | / +// 1 3 6 | 10 13 | 18 21 +// / \ / \ | / \ / \ | / \ +// 1 2 4 5| 8 9 11 12| 16 17 19 20 +// 0 1 3 4| 7 8 10 11| 15 16 18 19 +// | massif 0 | massif 1 . | massif 2 ....> + +// 1 << 3 - 1 << 2 = 8 - 4 = 4 +// 1 << 4 - 1 << 3 = 16 - 8 = 8 +// +// Massif Root Index 7-1 | 8+7-2 | 16 + 7-2 +// Massif Last Leaf Index 5-1 | 8+5-2 | 16 + 5-2 +// +// In order to require the power 2 property for the leaf count, we configure the +// massif size by its 'height'. Here, our 4 leaf tree has height 3 (level index 2) +// +// So typically instead of n + n -1, where n is the massif leaf count we instead do +// +// Massif Root Index = (1 << h) - 2 +// Massif Last Leaf Index = (1 << h) - h - 1 +type MassifContext struct { + TenantIdentity string + BlobPath string + Tags map[string]string + ETag string + LastRead time.Time + LastModfified time.Time + + // Read from the first log entry in the blob. If Creating is true and Found + // > 0, this is the Start header of the *previous* massif + Start MassifStart + Data []byte + + // the following properties are for dealing with addition of the last leaf + // in the massif they are only valid during the call to AddHashedLeaf which + // appends the last leaf of the massif (other appends are guaranteed not to + // reference nodes from earlier massif blobs) + + // Set to the peak stack index containing the *next* ancestor node that will + // be needed. Initialised in AddLeafHash and only valid during that call + nextAncestor int +} + +// Get returns the value associated with the node at MMR index i +// +// Note that due to the structure of the MMR we are guaranteed that adding a +// node will only reference other nodes in the *current* massif, OR it will +// reference the root of the previous massif. As we link the massif blobs by +// including the root of the previous massif as the value for the first massif +// entry, we can return it directly. Eg in fhe following, the left child of +// position 15 is the root of massif 0 at position 7, and similarly, the left +// child of the root of massif 2 will be position 15. As Get works in indices, +// that will be indices 14 and 6. +// +// 3 \ 15 massif 1 \ . massif 2 +// \/ \ \ +// massif 0 /\ \ | +// / \ \ | +// 2 ..... 7.....|....14........|...... 22 ..... +// / \ | / \ | / +// 1 3 6 | 10 13 | 18 21 +// / \ / \ | / \ / \ | / \ +// 1 2 4 5| 8 9 11 12| 16 17 19 20 +// 0 1 3 4| 7 8 10 11| 15 16 18 19 +// | massif 0 | massif 1 . | massif 2 ....> +// +// This method satisfies the Get method of the MMR NodeAdder interface +func (mc *MassifContext) Get(i uint64) ([]byte, error) { + + // Normal case, reference to a node included in the current massif + if i >= mc.Start.FirstIndex { + return IndexedLogValue(mc.Data[mc.LogStart():], i-mc.Start.FirstIndex), nil + } + + // Ok, its a reference to the root of the previous massif or this is an error case + + if mc.Start.FirstIndex == 0 { + return nil, fmt.Errorf("%w: the first massif has no ancestors", ErrGetIndexUnavailable) + } + + // The ancestor stack is maintained so that the nodes we need are listed in + // the order they will be asked for. And we initialise nextAncestor in + // AddLeafHash to the top of the stack + + if mc.nextAncestor < 0 { + return nil, fmt.Errorf("%w: exceeded the nodes included in the ancestor peak stack, requesting %d", ErrGetIndexUnavailable, i) + } + + stackTop := mc.LogStart() + stackStart := mc.PeakStackStart() + if stackStart > stackTop { + return nil, fmt.Errorf("%w: invalid context, requesting %d", ErrAncestorStackInvalid, i) + } + endOffset := (ValueBytes * (uint64(mc.nextAncestor) + 1)) + if endOffset > (stackTop - stackStart) { + return nil, fmt.Errorf("%w: exceeded the data range of the ancestor peak stack, requesting %d", ErrAncestorStackInvalid, i) + } + valueStart := stackTop - endOffset + mc.nextAncestor -= 1 + return mc.Data[valueStart : valueStart+ValueBytes], nil +} + +func (mc MassifContext) FixedHeaderEnd() uint64 { + return ValueBytes +} + +func (mc MassifContext) IndexHeaderStart() uint64 { + return mc.FixedHeaderEnd() +} + +// IndexHeaderEhd returns the end of the bytes reserved for the index header. +// Currently, nothing is stored in this. +// XXX: TODO: Consider removing the field all together +func (mc MassifContext) IndexHeaderEnd() uint64 { + return mc.IndexHeaderStart() + IndexHeaderBytes +} + +// IndexStart returns the index of the first **byte** of index data. +func (mc MassifContext) IndexStart() uint64 { + return mc.IndexHeaderEnd() +} + +func (mc MassifContext) IndexLen() uint64 { + return (1 << mc.Start.MassifHeight) +} + +func (mc MassifContext) IndexSize() uint64 { + return mc.IndexLen() * IndexEntryBytes +} + +// IndexEnd returns the byte index of the end of index data +func (mc MassifContext) IndexEnd() uint64 { + return mc.IndexStart() + IndexEntryBytes*(1< uint64(len(mc.Data)) { + return (uint64(len(mc.Data)) - logStart) / LogEntryBytes + } + return (uint64(len(mc.Data)) - logStart) / LogEntryBytes +} + +// RangeCount returns the total number of log entries in the MMR upto and including this context +func (mc MassifContext) RangeCount() uint64 { + return mc.Start.FirstIndex + mc.Count() +} + +// TreeRootIndex returns the root index for the tree with height +func TreeRootIndex(height uint8) uint64 { + return (1 << height) - 2 +} + +// RangeRootIndex return the Massif root node's index in the overall MMR given +// the massif height and the first index of the MMR it contains +func RangeRootIndex(firstIndex uint64, height uint8) uint64 { + return firstIndex + (1 << height) - 2 +} + +// RangeLastLeafIndex returns the mmr index of the last leaf given the first +// index of a massif and its height. +func RangeLastLeafIndex(firstIndex uint64, height uint8) uint64 { + return firstIndex + TreeLastLeafIndex(height) +} + +// TreeLastLeafIndex returns the index of the last leaf in the tree with the +// given height (1 << h) - h -1 works because the number of nodes required to +// include the last leaf is always equal to the MMR height produced by that +func TreeLastLeafIndex(height uint8) uint64 { + return (1 << height) - uint64(height) - 1 +} + +// TreeSize returns the maximum byte size of the tree based on the defined log +// entry size +func TreeSize(height uint8) uint64 { + return TreeCount(height) * LogEntryBytes +} + +// MaxCount returns the node count +func TreeCount(height uint8) uint64 { + return ((1 << height) - 1) +} diff --git a/mmrblobs/massifcontext_test.go b/mmrblobs/massifcontext_test.go new file mode 100644 index 0000000..715acb1 --- /dev/null +++ b/mmrblobs/massifcontext_test.go @@ -0,0 +1,87 @@ +package mmrblobs + +import ( + "testing" +) + +func TestMassifMaxCount(t *testing.T) { + type args struct { + height uint8 + } + tests := []struct { + name string + args args + want uint64 + }{ + {"height 3", args{3}, (8 - 1) * LogEntryBytes}, + {"height 4", args{4}, (16 - 1) * LogEntryBytes}, + {"height 8", args{8}, (256 - 1) * LogEntryBytes}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := TreeSize(tt.args.height); got != tt.want { + t.Errorf("TreeSize() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMassifLastLeafIndex(t *testing.T) { + type args struct { + firstIndex uint64 + height uint8 + } + tests := []struct { + name string + args args + want uint64 + }{ + {"m0, height 2", args{0, 2}, 1}, + {"m1, height 2", args{3, 2}, 4}, + {"m2, height 2", args{7, 2}, 8}, + + {"m0, height 3", args{0, 3}, 4}, + {"m1, height 3", args{7, 3}, 11}, + {"m2, height 3", args{15, 3}, 19}, + + {"m0, height 4", args{0, 4}, 11}, + {"m1, height 4", args{15, 4}, 26}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := RangeLastLeafIndex(tt.args.firstIndex, tt.args.height); got != tt.want { + t.Errorf("MassifLastLeafIndex() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMassifRootIndex(t *testing.T) { + type args struct { + firstIndex uint64 + height uint8 + } + tests := []struct { + name string + args args + want uint64 + }{ + {"m0, height 2", args{0, 2}, 3 - 1}, + {"m1, height 2", args{3, 2}, 6 - 1}, + {"m2, height 2", args{7, 2}, 10 - 1}, + + {"m0, height 3", args{0, 3}, 7 - 1}, + {"m1, height 3", args{7, 3}, 14 - 1}, + {"m2, height 3", args{15, 3}, 22 - 1}, + + {"m0, height 4", args{0, 4}, 15 - 1}, + {"m1, height 4", args{15, 4}, 30 - 1}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := RangeRootIndex(tt.args.firstIndex, tt.args.height); got != tt.want { + t.Errorf("MassifRootIndex() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/mmrblobs/massifstart.go b/mmrblobs/massifstart.go new file mode 100644 index 0000000..c1a4d12 --- /dev/null +++ b/mmrblobs/massifstart.go @@ -0,0 +1,187 @@ +package mmrblobs + +// Massif blobs are strictly sized as multiples of 32 bytes in order to +// facilitate simple content independent arithmetic operations over the whole +// MMR. +// +// Knowing only the relative resource name of the blob (which includes its +// epoch), and the size of the blob all information necessary to place it in the +// overall MMR can be derived computationaly (and efficiently) +// +// The massifstart is a 32 byte field encoding the small amount of book keeping +// required in a blob to allow for efficient correctness checks. This field is +// followed by the root hashes from preceding blobs that will be necessary to +// complete the blob. These are maintained in a stack. Neither the stack length +// nor a mapping of the positions it contains are stored, all of this +// information is recovered computationaly computed based on the blobs possition +// in the MMR +// +// The massif start field is also trie key compatible so that the start data can +// be included in the history indpendent proofs of exclusion and of +// completeness. + +import ( + "encoding/binary" + "errors" + "fmt" + + "github.com/datatrails/go-datatrails-merklelog/mmr" +) + +type KeyType uint8 + +const ( + KeyTypeApplicationContent KeyType = iota // this is the standard entry type, purposefuly defined as 0 + _ + _ + _ + _ + _ + _ + _ + KeyTypeApplicationLast // first 8 types are reserved for the application + // other entries are reserved for the MMR book keeping + + // KeyTypeInteriorNode trie keys for MMR interior nodes have this type + KeyTypeInteriorNode + + // KeyTypeMassifStart is the type for keys which correspond to massif blob + // header values + KeyTypeMassifStart + KeyTypeMax +) + +const ( + + // MassifStart layout + // + // . | type| | version| epoch |massif height| massif i | + // . | 0 | | 21 - 22 | 23 26|27 27| 28 - 31 | + // bytes | 1 | | 2 | 4 | 1 | 4 | + // + // Note this layout produces a sequentially valued key. The value is always + // considered as a big endian large integer. Lexical ordering is defined + // only for padded hex representations of the key value. The reserved zero + // bytes can be used in later versions. Because if we shift the version + // field left, even without incrementing it, the resulting key is + // numerically larger than all of those for previous versions + + MassifStartKeyVersionFirstByte = 21 + MassifStartKeyVersionSize = 2 // 16 bit + MassifStartKeyVersionEnd = MassifStartKeyVersionFirstByte + MassifStartKeyVersionSize + MassifStartKeyEpochFirstByte = MassifStartKeyVersionEnd + MassifStartKeyEpochSize = 4 // 32 bit + MassifStartKeyEpochEnd = MassifStartKeyEpochFirstByte + MassifStartKeyEpochSize + // Note the massif height is purposefully ahead of the index, it can't be + // changed without also incrementing the EPOCH, so we never care about it's + // effect on the sort order with respect to the first index + MassifStartKeyMassifHeightFirstByte = MassifStartKeyEpochEnd + MassifStartKeyMassifHeightSize = 1 // 8 bit + MassifStartKeyMassifHeightEnd = MassifStartKeyMassifHeightFirstByte + MassifStartKeyMassifHeightSize + + MassifStartKeyMassifFirstByte = MassifStartKeyMassifHeightEnd + MassifStartKeyMassifSize = 4 + MassifStartKeyMassifEnd = MassifStartKeyMassifFirstByte + MassifStartKeyMassifSize // 32 bit + MassifStartKeyFirstIndexFirstByte = MassifStartKeyMassifEnd + + MassifCurrentVersion = uint16(0) +) + +var ( + ErrMassifFixedHeaderMissing = errors.New("the fixed header for the massif is missing") + ErrMassifFixedHeaderBadType = errors.New("the fixed header for the massif has the wrong type code") + + ErrEntryTypeUnexpected = errors.New("the entry type was not as expected") + ErrEntryTypeInvalid = errors.New("the entry type was invalid") + ErrMassifBelowMinSize = errors.New("a massive blob always has at least three log entries") + ErrPrevRootNotSet = errors.New("the previous root was not provided") +) + +type MassifStart struct { + MassifHeight uint8 + Version uint16 + Epoch uint32 + MassifIndex uint32 + FirstIndex uint64 + PeakStackLen uint64 +} + +func NewMassifStart(epoch uint32, massifHeight uint8, massifIndex uint32, firstIndex uint64) MassifStart { + return MassifStart{ + Version: MassifCurrentVersion, + MassifHeight: massifHeight, + MassifIndex: massifIndex, + FirstIndex: firstIndex, + } +} + +// MassifFirstLeaf returns the MMR index of the first leaf in the massif blob identified by massifIndex +func MassifFirstLeaf(massifHeight uint8, massifIndex uint32) uint64 { + + // The number of leaves 'f' in a massif is derived from its height. + + // Given massif height, the number of m nodes is: + // m = (1 << h) - 1 + m := uint64((1 << massifHeight) - 1) + + // The size can be computed from the number of leaves f as + // m = f + f - 1 + // + // So to recover the number of f leaves in every massif in the epoch from m we have: + // f = (m + 1) / 2 + f := (m + 1) / 2 + + // So the first *leaf* index is then + leafIndex := f * uint64(massifIndex) + + // And now we can apply TreeIndex to the leaf index. This last is an + // iterative call but it is sub linear. Essentially its O(tree height) (not + // massif height ofc) + return mmr.TreeIndex(leafIndex) +} + +func (ms MassifStart) MarshalBinary() ([]byte, error) { + return EncodeMassifStart(ms.Version, ms.Epoch, ms.MassifHeight, ms.MassifIndex), nil +} + +func (ms *MassifStart) UnmarshalBinary(b []byte) error { + return DecodeMassifStart(ms, b) +} + +// EncodeMassifStart encodes the massif details in the prescribed massif header +// record format +// +// . | type| | version| epoch |massif height| massif i | +// . | 0 | | 21 - 22 | 23 26|27 27| 28 - 31 | +// bytes | 1 | | 2 | 4 | 1 | 4 | +func EncodeMassifStart(version uint16, epoch uint32, massifHeight uint8, massifIndex uint32) []byte { + key := [32]byte{} + + key[0] = byte(KeyTypeMassifStart) + + binary.BigEndian.PutUint16(key[MassifStartKeyVersionFirstByte:MassifStartKeyVersionEnd], version) + binary.BigEndian.PutUint32(key[MassifStartKeyEpochFirstByte:MassifStartKeyEpochEnd], epoch) + key[MassifStartKeyMassifHeightFirstByte] = massifHeight + binary.BigEndian.PutUint32(key[MassifStartKeyMassifFirstByte:MassifStartKeyMassifEnd], massifIndex) + return key[:] +} + +func DecodeMassifStart(ms *MassifStart, start []byte) error { + if len(start) < (ValueBytes) { + return ErrMassifFixedHeaderBadType + } + + if KeyType(start[0]) != KeyTypeMassifStart { + return fmt.Errorf("%w: %d", ErrMassifFixedHeaderBadType, start[0]) + } + + ms.Version = binary.BigEndian.Uint16(start[MassifStartKeyVersionFirstByte:MassifStartKeyVersionEnd]) + ms.Epoch = binary.BigEndian.Uint32(start[MassifStartKeyEpochFirstByte:MassifStartKeyEpochEnd]) + ms.MassifHeight = start[MassifStartKeyMassifHeightFirstByte] + + ms.MassifIndex = binary.BigEndian.Uint32(start[MassifStartKeyMassifFirstByte:MassifStartKeyMassifEnd]) + ms.FirstIndex = MassifFirstLeaf(ms.MassifHeight, ms.MassifIndex) + ms.PeakStackLen = mmr.LeafMinusSpurSum(uint64(ms.MassifIndex)) + + return nil +} diff --git a/mmrblobs/massifstart_test.go b/mmrblobs/massifstart_test.go new file mode 100644 index 0000000..c7e27ce --- /dev/null +++ b/mmrblobs/massifstart_test.go @@ -0,0 +1,61 @@ +package mmrblobs + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMassifFirstLeaf(t *testing.T) { + type args struct { + massifHeight uint8 + massifIndex uint32 + } + tests := []struct { + args args + want uint64 + }{ + {args{3, 0}, 0}, + {args{3, 1}, 7}, + {args{3, 2}, 15}, + {args{3, 3}, 22}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("(h, mi) = (%d,%d) = %d", tt.args.massifHeight, tt.args.massifIndex, tt.want), func(t *testing.T) { + if got := MassifFirstLeaf(tt.args.massifHeight, tt.args.massifIndex); got != tt.want { + t.Errorf("MassifFirstLeaf() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMassifStartKeyRoundTrip(t *testing.T) { + type args struct { + version uint16 + epoch uint32 + massifHeight uint8 + massifIndex uint32 + firstIndex uint64 + } + tests := []struct { + name string + args args + }{ + {"a", args{1, 2, 2, 2, 7}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + encoded := EncodeMassifStart(tt.args.version, tt.args.epoch, tt.args.massifHeight, tt.args.massifIndex) + encoded = append(encoded, make([]byte, 32)...) + got := MassifStart{} + err := got.UnmarshalBinary(encoded) + assert.Nil(t, err) + assert.Equal(t, got.Version, tt.args.version) + assert.Equal(t, got.Epoch, tt.args.epoch) + assert.Equal(t, got.MassifHeight, tt.args.massifHeight) + assert.Equal(t, got.MassifIndex, tt.args.massifIndex) + assert.Equal(t, got.FirstIndex, tt.args.firstIndex) + }) + } +} diff --git a/mmrblobs/masssifreader.go b/mmrblobs/masssifreader.go new file mode 100644 index 0000000..91e195c --- /dev/null +++ b/mmrblobs/masssifreader.go @@ -0,0 +1,117 @@ +package mmrblobs + +import ( + "context" + "time" + + "github.com/datatrails/go-datatrails-common/azblob" + "github.com/datatrails/go-datatrails-common/logger" + "github.com/datatrails/go-datatrails-merklelog/mmr" +) + +type massifReader interface { + Reader( + ctx context.Context, + identity string, + opts ...azblob.Option, + ) (*azblob.ReaderResponse, error) + + List(ctx context.Context, opts ...azblob.Option) (*azblob.ListerResponse, error) +} + +type MassifReader struct { + log logger.Logger + store massifReader +} + +func NewMassifReader(log logger.Logger, store massifReader) *MassifReader { + r := &MassifReader{ + log: log, + store: store, + } + return r +} + +func (mr *MassifReader) GetMassif( + ctx context.Context, tenantIdentity string, massifIndex uint64, + opts ...azblob.Option, +) (MassifContext, error) { + + var err error + var rr *azblob.ReaderResponse + mc := MassifContext{ + TenantIdentity: tenantIdentity, + BlobPath: TenantMassifBlobPath(tenantIdentity, massifIndex), + } + + rr, mc.Data, err = BlobRead(ctx, mc.BlobPath, mr.store, opts...) + if err != nil { + return MassifContext{}, err + } + mc.Tags = rr.Tags + mc.ETag = *rr.ETag + mc.LastRead = time.Now() + mc.LastModfified = *rr.LastModified + + err = mc.Start.UnmarshalBinary(mc.Data) + if err != nil { + return MassifContext{}, err + } + return mc, nil +} + +// MassifIndexFromLeafIndex gets the massif index of the massif that the given leaf is stored in, +// +// given the leaf index of the leaf. +// +// This is found with the given massif height, which is constant for all massifs. +func MassifIndexFromLeafIndex(massifHeight uint8, leafIndex uint64) uint64 { + + // first find how many leaf nodes each massif can hold. + // + // Note: massifHeight starts at index 1, whereas height index for HeighIndexLeafCount starts at 0. + massifMaxLeaves := mmr.HeightIndexLeafCount(uint64(massifHeight) - 1) + + // now find the massif. + // + // for context, see: https://github.com/datatrails/epic-8120-scalable-proof-mechanisms/blob/main/mmr/forestrie-mmrblobs.md#blob-size + // + // Note: massif indexes start at 0. + // Note: leaf indexes starts at 0. + // + // Therefore, given a massif height of 2, that has max leaves of 4; + // if a leaf index of 3 is given, then it is in massif 0, along with leaves, 0, 1 and 2. + return leafIndex / massifMaxLeaves + +} + +// MassifIndexFromMMRIndex gets the massif index of the massif that the given leaf is stored in +// +// given the mmr index of the leaf. +// +// NOTE: if the mmrIndex is not a leaf node, then error is returned. +func MassifIndexFromMMRIndex(massifHeight uint8, mmrIndex uint64) (uint64, error) { + + // First check if the given mmrIndex is a leaf node. + // + // NOTE: leaf nodes are always on height 0. + height := mmr.IndexHeight(mmrIndex) + if height != 0 { + return 0, ErrNotleaf + } + + // HeightSize returns the maximum number of nodes for a given height of MMR. Where the leaf nodes + // start on height 1. + mmrSize := mmr.HeightSize(uint64(massifHeight)) + + // now find the massif. + // + // for context, see: https://github.com/datatrails/epic-8120-scalable-proof-mechanisms/blob/main/mmr/forestrie-mmrblobs.md#blob-size + // + // Note: massif indexes start at 0. + // Note: mmr indexes starts at 0. + massifIndex := mmrIndex / mmrSize + + return massifIndex, nil + +} diff --git a/mmrblobs/masssifreader_test.go b/mmrblobs/masssifreader_test.go new file mode 100644 index 0000000..96754ae --- /dev/null +++ b/mmrblobs/masssifreader_test.go @@ -0,0 +1,197 @@ +package mmrblobs + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestMassifIndexFromLeafIndex tests: +// +// Example MMR for test 1,2,3. Derived from: https://github.com/datatrails/epic-8120-scalable-proof-mechanisms/blob/main/mmr/forestrie-mmrblobs.md#blob-size +// +// | 6 | 13 | 21 | height: 3 +// | / \ | / \ | / \ | +// | 2 5 | 9 12 | 17 20 | height: 2 +// | / \ / \ | / \ / \ | / \ / \ | +// |0 1 3 4|7 8 10 11|15 16 18 19| MMR INDICES height: 1 +// |----------|-------------|-------------| +// |0 1 2 3|4 5 6 7 | 8 9 10 11| LEAF INDICES +// |----------|-------------|-------------| +// | 0 | 1 | 2 | MASSIF INDICES +// |----------|-------------|-------------| +// +// 1. a height of 3 and a leaf index of 3, returns a massif index of 0 +// 2. a height of 3 and a leaf index of 10, returns a massif index of 2 +// 3. a height of 3 and a leaf index of 4, returns a massif index of 1 +// 4. a height of 5 and a leaf index of 25, returns a massif index of 1 +// 5. a height of 16 and a leaf index of 965235, returns a massif index of 29 +func TestMassifIndexFromLeafIndex(t *testing.T) { + type args struct { + massifHeight uint8 + leafIndex uint64 + } + tests := []struct { + name string + args args + expected uint64 + }{ + { + name: "height 2, leaf index 3", + args: args{ + massifHeight: 3, + leafIndex: 3, + }, + expected: 0, + }, + { + name: "height 2, leaf index 10", + args: args{ + massifHeight: 3, + leafIndex: 10, + }, + expected: 2, + }, + { + name: "height 2, leaf index 4", + args: args{ + massifHeight: 3, + leafIndex: 4, + }, + expected: 1, + }, + { + name: "height 4, leaf index 25", + args: args{ + massifHeight: 5, + leafIndex: 25, + }, + expected: 1, + }, + { + name: "height 4, leaf index 25", + args: args{ + massifHeight: 16, + leafIndex: 965235, + }, + expected: 29, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual := MassifIndexFromLeafIndex(test.args.massifHeight, test.args.leafIndex) + + assert.Equal(t, test.expected, actual) + }) + } +} + +// TestMassifIndexFromMMRIndex tests: +// +// Example MMR for test 1,2,3. Derived from: https://github.com/datatrails/epic-8120-scalable-proof-mechanisms/blob/main/mmr/forestrie-mmrblobs.md#blob-size +// +// | 6 | 13 | 21 | height: 3 +// | / \ | / \ | / \ | +// | 2 5 | 9 12 | 17 20 | height: 2 +// | / \ / \ | / \ / \ | / \ / \ | +// |0 1 3 4|7 8 10 11|15 16 18 19| MMR INDICES height: 1 +// |----------|-------------|-------------| +// |0 1 2 3|4 5 6 7 | 8 9 10 11| LEAF INDICES +// |----------|-------------|-------------| +// | 0 | 1 | 2 | MASSIF INDICES +// |----------|-------------|-------------| +// +// 1. a height of 3 and a mmr index of 4, returns a massif index of 0 +// 2. a height of 3 and a mmr index of 15, returns a massif index of 2 +// 3. a height of 3 and a mmr index of 10, returns a massif index of 1 +// 4. a height of 5 and a mmr index of 33, returns a massif index of 1 +// 5. a height of 5 and a mmr index of 70, returns a massif index of 1 +// 6. a height of 3 and a mmr index of 12, returns not a leaf err +// 7. a height of 5 and a mmr index of 72, returns not a leaf err +func TestMassifIndexFromMMRIndex(t *testing.T) { + type args struct { + massifHeight uint8 + mmrIndex uint64 + } + tests := []struct { + name string + args args + expected uint64 + err error + }{ + { + name: "height 3, mmr index 4", + args: args{ + massifHeight: 3, + mmrIndex: 4, + }, + expected: 0, + }, + { + name: "height 3, mmr index 15", + args: args{ + massifHeight: 3, + mmrIndex: 15, + }, + expected: 2, + }, + { + name: "height 3, mmr index 10", + args: args{ + massifHeight: 3, + mmrIndex: 10, + }, + expected: 1, + }, + { + name: "height 5, mmr index 32", + args: args{ + massifHeight: 5, + mmrIndex: 32, + }, + expected: 1, + }, + { + name: "height 5, mmr index 70", + args: args{ + massifHeight: 5, + mmrIndex: 70, + }, + expected: 2, + }, + { + name: "height 5, mmr index 70", + args: args{ + massifHeight: 5, + mmrIndex: 70, + }, + expected: 2, + }, + { + name: "height 3, mmr index 12", + args: args{ + massifHeight: 3, + mmrIndex: 12, + }, + expected: 0, + err: ErrNotleaf, + }, + { + name: "height 5, mmr index 72", + args: args{ + massifHeight: 5, + mmrIndex: 72, + }, + expected: 0, + err: ErrNotleaf, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual, err := MassifIndexFromMMRIndex(test.args.massifHeight, test.args.mmrIndex) + + assert.Equal(t, test.err, err) + assert.Equal(t, test.expected, actual) + }) + } +} diff --git a/mmrblobs/tenantblobpaths.go b/mmrblobs/tenantblobpaths.go new file mode 100644 index 0000000..b7812d0 --- /dev/null +++ b/mmrblobs/tenantblobpaths.go @@ -0,0 +1,34 @@ +package mmrblobs + +import "fmt" + +const ( + V1MMRPrefix = "v1/mmrs" + V1MMRBlobNameFmt = "%016d.log" +) + +// DataTrails Specifics of managing MMR's in azure blob storage + +func TenantMassifPrefix(tenantIdentity string) string { + return fmt.Sprintf( + "%s/%s/massifs/", V1MMRPrefix, tenantIdentity, + ) + +} + +// TenantEpochMountainBlobPath returns the appropriate blob path for the blob +// +// We partition the blob space conveniently for working with the double batched +// merkle log accumulator scheme described by +// Justin Drake [here](https://ethresear.ch/t/double-batched-merkle-log-accumulator/571) +// +// The returned string forms a relative resource name with a versioned resource +// prefix of 'v1/mmrs' +// +// Because azure blob names and tags sort and compare only *lexically*, The +// number is represented in that path as a 16 digit hex string. +func TenantMassifBlobPath(tenantIdentity string, number uint64) string { + return fmt.Sprintf( + "%s%s", TenantMassifPrefix(tenantIdentity), fmt.Sprintf(V1MMRBlobNameFmt, number), + ) +}