diff --git a/backfill/Dockerfile b/backfill/Dockerfile new file mode 100644 index 00000000..629825ee --- /dev/null +++ b/backfill/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.23.3 AS builder +WORKDIR /app +COPY . . +RUN go mod download +RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o main . + +FROM scratch +WORKDIR /app +COPY ./global-bundle.pem ./global-bundle.pem +COPY --from=builder /app/main . +CMD ["./main"] diff --git a/backfill/Dockerfile.indexes b/backfill/Dockerfile.indexes new file mode 100644 index 00000000..431b50ff --- /dev/null +++ b/backfill/Dockerfile.indexes @@ -0,0 +1,11 @@ +FROM golang:1.23.3 AS builder +WORKDIR /app +COPY . . +RUN go mod download +RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o recreate-indexes ./recreate-indexes/recreate-indexes.go + +FROM scratch +WORKDIR /app +COPY ./global-bundle.pem ./global-bundle.pem +COPY --from=builder /app/recreate-indexes . +CMD ["./recreate-indexes"] diff --git a/backfill/Dockerfile.middle-backfill b/backfill/Dockerfile.middle-backfill new file mode 100644 index 00000000..7cc9bce1 --- /dev/null +++ b/backfill/Dockerfile.middle-backfill @@ -0,0 +1,11 @@ +FROM golang:1.23.3 AS builder +WORKDIR /app +COPY . . +RUN go mod download +RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o middle-backfill ./middle-backfill/middle-backfill.go + +FROM scratch +WORKDIR /app +COPY ./global-bundle.pem ./global-bundle.pem +COPY --from=builder /app/middle-backfill . +CMD ["./middle-backfill"] diff --git a/backfill/config/db.go b/backfill/config/db.go new file mode 100644 index 00000000..de8a7f7a --- /dev/null +++ b/backfill/config/db.go @@ -0,0 +1,34 @@ +package config + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/jackc/pgx/v5/pgxpool" +) + +func InitDatabase() *pgxpool.Pool { + env := GetConfig() + connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", + env.DbHost, env.DbPort, env.DbUser, env.DbPassword, env.DbName) + + config, err := pgxpool.ParseConfig(connStr) + if err != nil { + log.Fatalf("Unable to parse connection string: %v\n", err) + } + + config.MaxConns = 2 // Maximum number of connections + config.MinConns = 1 // Minimum number of connections to keep alive + config.MaxConnLifetime = 1 * time.Hour // Close and refresh connections after 1 hour + config.HealthCheckPeriod = 1 * time.Minute // Check connection health every minute + + // Create the pool + pool, err := pgxpool.NewWithConfig(context.Background(), config) + if err != nil { + log.Fatalf("Unable to connect to database: %v\n", err) + } + + return pool +} diff --git a/backfill/config/env.go b/backfill/config/env.go new file mode 100644 index 00000000..83922926 --- /dev/null +++ b/backfill/config/env.go @@ -0,0 +1,77 @@ +package config + +import ( + "log" + "os" + "strconv" + + "github.com/joho/godotenv" +) + +type Config struct { + DbUser string + DbPassword string + DbName string + DbHost string + DbPort string + CertPath string + Network string + ChainId int + SyncBaseUrl string + SyncMinHeight int + SyncFetchIntervalInBlocks int + SyncAttemptsMaxRetry int + SyncAttemptsIntervalInMs int + IsDevelopment bool +} + +var config *Config + +func InitEnv(envFilePath string) { + IsDevelopment := true + if err := godotenv.Load(envFilePath); err != nil { + IsDevelopment = false + log.Printf("No .env file found at %s, falling back to system environment variables", envFilePath) + } + + config = &Config{ + DbUser: getEnv("DB_USER"), + DbPassword: getEnv("DB_PASSWORD"), + DbName: getEnv("DB_NAME"), + DbHost: getEnv("DB_HOST"), + DbPort: getEnv("DB_PORT"), + CertPath: getEnv("CERT_PATH"), + Network: getEnv("NETWORK"), + ChainId: getEnvAsInt("CHAIN_ID"), + SyncBaseUrl: getEnv("SYNC_BASE_URL"), + SyncMinHeight: getEnvAsInt("SYNC_MIN_HEIGHT"), + SyncFetchIntervalInBlocks: getEnvAsInt("SYNC_FETCH_INTERVAL_IN_BLOCKS"), + SyncAttemptsMaxRetry: getEnvAsInt("SYNC_ATTEMPTS_MAX_RETRY"), + SyncAttemptsIntervalInMs: getEnvAsInt("SYNC_ATTEMPTS_INTERVAL_IN_MS"), + IsDevelopment: IsDevelopment, + } +} + +func GetConfig() *Config { + if config == nil { + log.Fatal("Config not initialized. Call InitEnv first.") + } + return config +} + +func getEnv(key string) string { + value := os.Getenv(key) + if value == "" { + log.Fatalf("Environment variable %s is required but not set", key) + } + return value +} + +func getEnvAsInt(key string) int { + valueStr := getEnv(key) + value, err := strconv.Atoi(valueStr) + if err != nil { + log.Fatalf("Environment variable %s must be an integer, but got: %s", key, valueStr) + } + return value +} diff --git a/backfill/config/memory-monitor.go b/backfill/config/memory-monitor.go new file mode 100644 index 00000000..2e293505 --- /dev/null +++ b/backfill/config/memory-monitor.go @@ -0,0 +1,25 @@ +package config + +import ( + "log" + "runtime" + "time" +) + +func StartMemoryMonitoring() { + var memStats runtime.MemStats + for { + runtime.ReadMemStats(&memStats) + + log.Printf( + "Alloc: %v KB, Sys: %v KB, HeapIdle: %v KB, HeapInuse: %v KB, NumGC: %v\n", + memStats.Alloc/1024, // Total allocated memory + memStats.Sys/1024, // Total system memory requested + memStats.HeapIdle/1024, // Idle heap memory + memStats.HeapInuse/1024, // In-use heap memory + memStats.NumGC, // Number of garbage collections + ) + + time.Sleep(10 * time.Second) + } +} diff --git a/backfill/fetch/fetch_cut.go b/backfill/fetch/fetch_cut.go new file mode 100644 index 00000000..c5685f2e --- /dev/null +++ b/backfill/fetch/fetch_cut.go @@ -0,0 +1,93 @@ +package fetch + +import ( + "encoding/json" + "fmt" + "go-backfill/config" + "io" + "log" + "net/http" + "strconv" +) + +type FetchCutResult struct { + Hashes map[string]struct { + Height int `json:"height"` + Hash string `json:"hash"` + } `json:"hashes"` +} + +type CutResult struct { + Hash string + Height int +} + +func FetchCut() CutResult { + env := config.GetConfig() + + endpoint := fmt.Sprintf("%s/%s/cut", env.SyncBaseUrl, env.Network) + + resp, err := http.Get(endpoint) + if err != nil { + log.Fatalf("error making GET request: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + log.Fatalf("Unexpected status code: %d, body: %s", resp.StatusCode, string(body)) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatalf("error reading response body: %v", err) + } + + var result FetchCutResult + err = json.Unmarshal(body, &result) + if err != nil { + log.Fatalf("Error parsing JSON response: %v", err) + } + + ChainId := strconv.Itoa(env.ChainId) + lastHeight := result.Hashes[ChainId].Height + // if lastHeight == nil { + // return 0, fmt.Errorf("no height found: %w", err) + // } + res := CutResult{ + Hash: result.Hashes[ChainId].Hash, + Height: lastHeight, + } + + return res +} + +func FetchCuts() FetchCutResult { + env := config.GetConfig() + + endpoint := fmt.Sprintf("%s/%s/cut", env.SyncBaseUrl, env.Network) + + resp, err := http.Get(endpoint) + if err != nil { + log.Fatalf("error making GET request: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + log.Fatalf("Unexpected status code: %d, body: %s", resp.StatusCode, string(body)) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatalf("error reading response body: %v", err) + } + + var result FetchCutResult + err = json.Unmarshal(body, &result) + if err != nil { + log.Fatalf("Error parsing JSON response: %v", err) + } + + return result +} diff --git a/backfill/fetch/fetch_payloads_with_headers.go b/backfill/fetch/fetch_payloads_with_headers.go new file mode 100644 index 00000000..79513d75 --- /dev/null +++ b/backfill/fetch/fetch_payloads_with_headers.go @@ -0,0 +1,126 @@ +package fetch + +import ( + "bytes" + "encoding/json" + "fmt" + "go-backfill/config" + "io" + "log" + "net/http" + "time" +) + +type BlockInfo struct { + Header Header `json:"header"` + Payload Payload `json:"payloadWithOutputs"` +} + +type Adjacents map[string]string + +type Header struct { + Nonce string `json:"nonce"` + CreationTime int64 `json:"creationTime"` + Parent string `json:"parent"` + Adjacents Adjacents `json:"adjacents"` + Target string `json:"target"` + PayloadHash string `json:"payloadHash"` + ChainId int `json:"chainId"` + Weight string `json:"weight"` + Height int `json:"height"` + ChainwebVersion string `json:"chainwebVersion"` + EpochStart int64 `json:"epochStart"` + FeatureFlags uint64 `json:"featureFlags"` + Hash string `json:"hash"` +} + +type Payload struct { + Transactions [][2]string `json:"transactions"` + MinerData string `json:"minerData"` + TransactionsHash string `json:"transactionsHash"` + OutputsHash string `json:"outputsHash"` + PayloadHash string `json:"payloadHash"` + Coinbase string `json:"coinbase"` +} + +func FetchPayloadsWithHeaders(network string, chainId int, Hash string, minHeight int, maxHeight int) ([]BlockInfo, error) { + type FetchResponse struct { + Items []BlockInfo `json:"items"` + } + + startTime := time.Now() + env := config.GetConfig() + endpoint := fmt.Sprintf("%s/%s/chain/%d/block/branch?minheight=%d&maxheight=%d", env.SyncBaseUrl, network, chainId, minHeight, maxHeight) + + param := map[string]interface{}{ + "upper": []string{Hash}, + } + + paramJSON, err := json.Marshal(param) + if err != nil { + return nil, fmt.Errorf("failed to marshal payload hashes to JSON: %v", err) + } + + attempt := 1 + for attempt <= env.SyncAttemptsMaxRetry { + req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(paramJSON)) + if err != nil { + return nil, fmt.Errorf("failed to create request: %v", err) + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Accept", "application/json") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Printf("Attempt %d: Error making POST request for payloads: %v\n", attempt, err) + if attempt == env.SyncAttemptsMaxRetry { + return nil, err + } + + attempt++ + time.Sleep(time.Duration(env.SyncAttemptsIntervalInMs) * time.Millisecond) + continue + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + log.Printf("Attempt %d: Received non-OK HTTP status %d\n", attempt, resp.StatusCode) + if attempt == env.SyncAttemptsMaxRetry { + return nil, fmt.Errorf("received non-OK HTTP status: %d", resp.StatusCode) + } + + attempt++ + time.Sleep(time.Duration(env.SyncAttemptsIntervalInMs) * time.Millisecond) + continue + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %v", err) + } + + var payload FetchResponse + err = json.Unmarshal(body, &payload) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal JSON response: %v", err) + } + + if len(payload.Items) == 0 { + log.Printf("Attempt %d: No payloads found, retrying...\n", attempt) + if attempt == env.SyncAttemptsMaxRetry { + return nil, fmt.Errorf("no payloads found after maximum attempts: %v", err) + } + + attempt++ + time.Sleep(time.Duration(env.SyncAttemptsIntervalInMs) * time.Millisecond) + continue + } + + log.Printf("Fetched payloads in %fs\n", time.Since(startTime).Seconds()) + return payload.Items, nil + } + + return nil, fmt.Errorf("failed to fetch payloads after maximum retry attempts: %v", err) +} diff --git a/backfill/fetch/process_payloads.go b/backfill/fetch/process_payloads.go new file mode 100644 index 00000000..08d0a6dd --- /dev/null +++ b/backfill/fetch/process_payloads.go @@ -0,0 +1,173 @@ +package fetch + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "log" + "strings" + "time" +) + +type Event struct { + Params []interface{} `json:"params"` + Name string `json:"name"` + Module Module `json:"module"` + ModuleHash string `json:"moduleHash"` +} + +type Module struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +type Result struct { + Status string `json:"status"` + Data interface{} `json:"data"` +} + +type ProcessedPayload struct { + Header Header `json:"header"` + Transactions []DecodedTransaction `json:"transactions"` + MinerData json.RawMessage `json:"minerData"` + TransactionsHash string `json:"transactionsHash"` + OutputsHash string `json:"outputsHash"` + PayloadHash string `json:"payloadHash"` + Coinbase json.RawMessage `json:"coinbase"` +} + +type DecodedTransaction struct { + Hash string `json:"hash"` + Sigs json.RawMessage `json:"sigs"` + Cmd json.RawMessage `json:"cmd"` + Gas int `json:"gas"` + Result json.RawMessage `json:"result"` + ReqKey string `json:"reqKey"` + Logs string `json:"logs"` + Events []Event `json:"events"` + Continuation json.RawMessage `json:"continuation"` + Step int `json:"step"` + TTL string `json:"ttl"` + TxId int `json:"txId"` +} + +type TransactionPart0 struct { + Hash string `json:"hash"` + Sigs json.RawMessage `json:"sigs"` + Cmd json.RawMessage `json:"cmd"` +} + +type TransactionPart1 struct { + Gas int `json:"gas"` + Result json.RawMessage `json:"result"` + ReqKey string `json:"reqKey"` + Logs string `json:"logs"` + Events []Event `json:"events"` + Continuation json.RawMessage `json:"continuation"` + TxId int `json:"txId"` +} + +func ProcessPayloads(blocks []BlockInfo) ([]ProcessedPayload, error) { + startTime := time.Now() + var processedPayloads []ProcessedPayload + + for _, block := range blocks { + payload := block.Payload + var transactions []DecodedTransaction + + for _, transactionParts := range payload.Transactions { + if len(transactionParts) != 2 { + log.Printf("Transaction parts length is not 2, skipping transaction") + continue + } + + var part0 TransactionPart0 + var part1 TransactionPart1 + + err := DecodeBase64AndParseJSON(transactionParts[0], &part0) + if err != nil { + return nil, fmt.Errorf("error decoding transaction part 0: %w", err) + } + + err = DecodeBase64AndParseJSON(string(transactionParts[1]), &part1) + if err != nil { + return nil, fmt.Errorf("error decoding transaction part 1: %w", err) + } + + transactions = append(transactions, DecodedTransaction{ + Hash: part0.Hash, + Sigs: part0.Sigs, + Cmd: part0.Cmd, + Gas: part1.Gas, + Result: part1.Result, + ReqKey: part1.ReqKey, + Logs: part1.Logs, + Events: part1.Events, + Continuation: part1.Continuation, + TxId: part1.TxId, + }) + } + + var minerDataRaw json.RawMessage + err1 := DecodeBase64AndParseJSON(payload.MinerData, &minerDataRaw) + if err1 != nil { + return nil, fmt.Errorf("error decoding miner data: %w", err1) + } + + var coinbaseRaw json.RawMessage + err2 := DecodeBase64AndParseJSON(payload.Coinbase, &coinbaseRaw) + if err2 != nil { + return nil, fmt.Errorf("error decoding coinbase data: %w", err2) + } + + processedPayload := ProcessedPayload{ + Header: block.Header, + Transactions: transactions, + MinerData: minerDataRaw, + TransactionsHash: payload.TransactionsHash, + OutputsHash: payload.OutputsHash, + PayloadHash: payload.PayloadHash, + Coinbase: coinbaseRaw, + } + + processedPayloads = append(processedPayloads, processedPayload) + } + + log.Printf("Processed payloads in %fs\n", time.Since(startTime).Seconds()) + return processedPayloads, nil +} + +func DecodeBase64AndParseJSON(encodedData string, v interface{}) error { + // Normalize the input by ensuring proper padding + encodedData = ensureBase64Padding(encodedData) + + // Attempt decoding using both standard and URL-safe Base64 encodings + var decodedData []byte + var err error + + decodedData, err = base64.StdEncoding.DecodeString(encodedData) + if err != nil { + decodedData, err = base64.URLEncoding.DecodeString(encodedData) + if err != nil { + return fmt.Errorf("error decoding base64 data using both standard and URL-safe encodings: %w", err) + } + } + + // Unmarshal the JSON data into the provided interface + err = json.Unmarshal(decodedData, v) + if err != nil { + return fmt.Errorf("error unmarshalling JSON data: %w", err) + } + + return nil +} + +// ensureBase64Padding adds missing padding to a Base64 string if necessary. +func ensureBase64Padding(base64Str string) string { + missingPadding := len(base64Str) % 4 + if missingPadding > 0 { + padding := strings.Repeat("=", 4-missingPadding) + base64Str += padding + } + return base64Str +} diff --git a/backfill/global-bundle.pem b/backfill/global-bundle.pem new file mode 100644 index 00000000..de68d41a --- /dev/null +++ b/backfill/global-bundle.pem @@ -0,0 +1,3028 @@ +-----BEGIN CERTIFICATE----- +MIIEEjCCAvqgAwIBAgIJAM2ZN/+nPi27MA0GCSqGSIb3DQEBCwUAMIGVMQswCQYD +VQQGEwJVUzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEi +MCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1h +em9uIFJEUzEmMCQGA1UEAwwdQW1hem9uIFJEUyBhZi1zb3V0aC0xIFJvb3QgQ0Ew +HhcNMTkxMDI4MTgwNTU4WhcNMjQxMDI2MTgwNTU4WjCBlTELMAkGA1UEBhMCVVMx +EDAOBgNVBAcMB1NlYXR0bGUxEzARBgNVBAgMCldhc2hpbmd0b24xIjAgBgNVBAoM +GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx +JjAkBgNVBAMMHUFtYXpvbiBSRFMgYWYtc291dGgtMSBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwR2351uPMZaJk2gMGT+1sk8HE9MQh2rc +/sCnbxGn2p1c7Oi9aBbd/GiFijeJb2BXvHU+TOq3d3Jjqepq8tapXVt4ojbTJNyC +J5E7r7KjTktKdLxtBE1MK25aY+IRJjtdU6vG3KiPKUT1naO3xs3yt0F76WVuFivd +9OHv2a+KHvPkRUWIxpmAHuMY9SIIMmEZtVE7YZGx5ah0iO4JzItHcbVR0y0PBH55 +arpFBddpIVHCacp1FUPxSEWkOpI7q0AaU4xfX0fe1BV5HZYRKpBOIp1TtZWvJD+X +jGUtL1BEsT5vN5g9MkqdtYrC+3SNpAk4VtpvJrdjraI/hhvfeXNnAwIDAQABo2Mw +YTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUEEi/ +WWMcBJsoGXg+EZwkQ0MscZQwHwYDVR0jBBgwFoAUEEi/WWMcBJsoGXg+EZwkQ0Ms +cZQwDQYJKoZIhvcNAQELBQADggEBAGDZ5js5Pc/gC58LJrwMPXFhJDBS8QuDm23C +FFUdlqucskwOS3907ErK1ZkmVJCIqFLArHqskFXMAkRZ2PNR7RjWLqBs+0znG5yH +hRKb4DXzhUFQ18UBRcvT6V6zN97HTRsEEaNhM/7k8YLe7P8vfNZ28VIoJIGGgv9D +wQBBvkxQ71oOmAG0AwaGD0ORGUfbYry9Dz4a4IcUsZyRWRMADixgrFv6VuETp26s +/+z+iqNaGWlELBKh3iQCT6Y/1UnkPLO42bxrCSyOvshdkYN58Q2gMTE1SVTqyo8G +Lw8lLAz9bnvUSgHzB3jRrSx6ggF/WRMRYlR++y6LXP4SAsSAaC0= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEEjCCAvqgAwIBAgIJAJYM4LxvTZA6MA0GCSqGSIb3DQEBCwUAMIGVMQswCQYD +VQQGEwJVUzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEi +MCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1h +em9uIFJEUzEmMCQGA1UEAwwdQW1hem9uIFJEUyBldS1zb3V0aC0xIFJvb3QgQ0Ew +HhcNMTkxMDMwMjAyMDM2WhcNMjQxMDI4MjAyMDM2WjCBlTELMAkGA1UEBhMCVVMx +EDAOBgNVBAcMB1NlYXR0bGUxEzARBgNVBAgMCldhc2hpbmd0b24xIjAgBgNVBAoM +GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx +JjAkBgNVBAMMHUFtYXpvbiBSRFMgZXUtc291dGgtMSBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqM921jXCXeqpRNCS9CBPOe5N7gMaEt+D +s5uR3riZbqzRlHGiF1jZihkXfHAIQewDwy+Yz+Oec1aEZCQMhUHxZJPusuX0cJfj +b+UluFqHIijL2TfXJ3D0PVLLoNTQJZ8+GAPECyojAaNuoHbdVqxhOcznMsXIXVFq +yVLKDGvyKkJjai/iSPDrQMXufg3kWt0ISjNLvsG5IFXgP4gttsM8i0yvRd4QcHoo +DjvH7V3cS+CQqW5SnDrGnHToB0RLskE1ET+oNOfeN9PWOxQprMOX/zmJhnJQlTqD +QP7jcf7SddxrKFjuziFiouskJJyNDsMjt1Lf60+oHZhed2ogTeifGwIDAQABo2Mw +YTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUFBAF +cgJe/BBuZiGeZ8STfpkgRYQwHwYDVR0jBBgwFoAUFBAFcgJe/BBuZiGeZ8STfpkg +RYQwDQYJKoZIhvcNAQELBQADggEBAKAYUtlvDuX2UpZW9i1QgsjFuy/ErbW0dLHU +e/IcFtju2z6RLZ+uF+5A8Kme7IKG1hgt8s+w9TRVQS/7ukQzoK3TaN6XKXRosjtc +o9Rm4gYWM8bmglzY1TPNaiI4HC7546hSwJhubjN0bXCuj/0sHD6w2DkiGuwKNAef +yTu5vZhPkeNyXLykxkzz7bNp2/PtMBnzIp+WpS7uUDmWyScGPohKMq5PqvL59z+L +ZI3CYeMZrJ5VpXUg3fNNIz/83N3G0sk7wr0ohs/kHTP7xPOYB0zD7Ku4HA0Q9Swf +WX0qr6UQgTPMjfYDLffI7aEId0gxKw1eGYc6Cq5JAZ3ipi/cBFc= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEEjCCAvqgAwIBAgIJANew34ehz5l8MA0GCSqGSIb3DQEBCwUAMIGVMQswCQYD +VQQGEwJVUzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEi +MCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1h +em9uIFJEUzEmMCQGA1UEAwwdQW1hem9uIFJEUyBtZS1zb3V0aC0xIFJvb3QgQ0Ew +HhcNMTkwNTEwMjE0ODI3WhcNMjQwNTA4MjE0ODI3WjCBlTELMAkGA1UEBhMCVVMx +EDAOBgNVBAcMB1NlYXR0bGUxEzARBgNVBAgMCldhc2hpbmd0b24xIjAgBgNVBAoM +GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx +JjAkBgNVBAMMHUFtYXpvbiBSRFMgbWUtc291dGgtMSBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp7BYV88MukcY+rq0r79+C8UzkT30fEfT +aPXbx1d6M7uheGN4FMaoYmL+JE1NZPaMRIPTHhFtLSdPccInvenRDIatcXX+jgOk +UA6lnHQ98pwN0pfDUyz/Vph4jBR9LcVkBbe0zdoKKp+HGbMPRU0N2yNrog9gM5O8 +gkU/3O2csJ/OFQNnj4c2NQloGMUpEmedwJMOyQQfcUyt9CvZDfIPNnheUS29jGSw +ERpJe/AENu8Pxyc72jaXQuD+FEi2Ck6lBkSlWYQFhTottAeGvVFNCzKszCntrtqd +rdYUwurYsLTXDHv9nW2hfDUQa0mhXf9gNDOBIVAZugR9NqNRNyYLHQIDAQABo2Mw +YTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU54cf +DjgwBx4ycBH8+/r8WXdaiqYwHwYDVR0jBBgwFoAU54cfDjgwBx4ycBH8+/r8WXda +iqYwDQYJKoZIhvcNAQELBQADggEBAIIMTSPx/dR7jlcxggr+O6OyY49Rlap2laKA +eC/XI4ySP3vQkIFlP822U9Kh8a9s46eR0uiwV4AGLabcu0iKYfXjPkIprVCqeXV7 +ny9oDtrbflyj7NcGdZLvuzSwgl9SYTJp7PVCZtZutsPYlbJrBPHwFABvAkMvRtDB +hitIg4AESDGPoCl94sYHpfDfjpUDMSrAMDUyO6DyBdZH5ryRMAs3lGtsmkkNUrso +aTW6R05681Z0mvkRdb+cdXtKOSuDZPoe2wJJIaz3IlNQNSrB5TImMYgmt6iAsFhv +3vfTSTKrZDNTJn4ybG6pq1zWExoXsktZPylJly6R3RBwV6nwqBM= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEBjCCAu6gAwIBAgIJAMc0ZzaSUK51MA0GCSqGSIb3DQEBCwUAMIGPMQswCQYD +VQQGEwJVUzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEi +MCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1h +em9uIFJEUzEgMB4GA1UEAwwXQW1hem9uIFJEUyBSb290IDIwMTkgQ0EwHhcNMTkw +ODIyMTcwODUwWhcNMjQwODIyMTcwODUwWjCBjzELMAkGA1UEBhMCVVMxEDAOBgNV +BAcMB1NlYXR0bGUxEzARBgNVBAgMCldhc2hpbmd0b24xIjAgBgNVBAoMGUFtYXpv +biBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxIDAeBgNV +BAMMF0FtYXpvbiBSRFMgUm9vdCAyMDE5IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEArXnF/E6/Qh+ku3hQTSKPMhQQlCpoWvnIthzX6MK3p5a0eXKZ +oWIjYcNNG6UwJjp4fUXl6glp53Jobn+tWNX88dNH2n8DVbppSwScVE2LpuL+94vY +0EYE/XxN7svKea8YvlrqkUBKyxLxTjh+U/KrGOaHxz9v0l6ZNlDbuaZw3qIWdD/I +6aNbGeRUVtpM6P+bWIoxVl/caQylQS6CEYUk+CpVyJSkopwJlzXT07tMoDL5WgX9 +O08KVgDNz9qP/IGtAcRduRcNioH3E9v981QO1zt/Gpb2f8NqAjUUCUZzOnij6mx9 +McZ+9cWX88CRzR0vQODWuZscgI08NvM69Fn2SQIDAQABo2MwYTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUc19g2LzLA5j0Kxc0LjZa +pmD/vB8wHwYDVR0jBBgwFoAUc19g2LzLA5j0Kxc0LjZapmD/vB8wDQYJKoZIhvcN +AQELBQADggEBAHAG7WTmyjzPRIM85rVj+fWHsLIvqpw6DObIjMWokpliCeMINZFV +ynfgBKsf1ExwbvJNzYFXW6dihnguDG9VMPpi2up/ctQTN8tm9nDKOy08uNZoofMc +NUZxKCEkVKZv+IL4oHoeayt8egtv3ujJM6V14AstMQ6SwvwvA93EP/Ug2e4WAXHu +cbI1NAbUgVDqp+DRdfvZkgYKryjTWd/0+1fS8X1bBZVWzl7eirNVnHbSH2ZDpNuY +0SBd8dj5F6ld3t58ydZbrTHze7JJOd8ijySAp4/kiu9UfZWuTPABzDa/DSdz9Dk/ +zPW4CXXvhLmE02TA9/HeCw3KEHIwicNuEfw= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEEDCCAvigAwIBAgIJAKFMXyltvuRdMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD +VQQGEwJVUzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEi +MCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1h +em9uIFJEUzElMCMGA1UEAwwcQW1hem9uIFJEUyBCZXRhIFJvb3QgMjAxOSBDQTAe +Fw0xOTA4MTkxNzM4MjZaFw0yNDA4MTkxNzM4MjZaMIGUMQswCQYDVQQGEwJVUzEQ +MA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEiMCAGA1UECgwZ +QW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEl +MCMGA1UEAwwcQW1hem9uIFJEUyBCZXRhIFJvb3QgMjAxOSBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMkZdnIH9ndatGAcFo+DppGJ1HUt4x+zeO+0 +ZZ29m0sfGetVulmTlv2d5b66e+QXZFWpcPQMouSxxYTW08TbrQiZngKr40JNXftA +atvzBqIImD4II0ZX5UEVj2h98qe/ypW5xaDN7fEa5e8FkYB1TEemPaWIbNXqchcL +tV7IJPr3Cd7Z5gZJlmujIVDPpMuSiNaal9/6nT9oqN+JSM1fx5SzrU5ssg1Vp1vv +5Xab64uOg7wCJRB9R2GC9XD04odX6VcxUAGrZo6LR64ZSifupo3l+R5sVOc5i8NH +skdboTzU9H7+oSdqoAyhIU717PcqeDum23DYlPE2nGBWckE+eT8CAwEAAaNjMGEw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFK2hDBWl +sbHzt/EHd0QYOooqcFPhMB8GA1UdIwQYMBaAFK2hDBWlsbHzt/EHd0QYOooqcFPh +MA0GCSqGSIb3DQEBCwUAA4IBAQAO/718k8EnOqJDx6wweUscGTGL/QdKXUzTVRAx +JUsjNUv49mH2HQVEW7oxszfH6cPCaupNAddMhQc4C/af6GHX8HnqfPDk27/yBQI+ +yBBvIanGgxv9c9wBbmcIaCEWJcsLp3HzXSYHmjiqkViXwCpYfkoV3Ns2m8bp+KCO +y9XmcCKRaXkt237qmoxoh2sGmBHk2UlQtOsMC0aUQ4d7teAJG0q6pbyZEiPyKZY1 +XR/UVxMJL0Q4iVpcRS1kaNCMfqS2smbLJeNdsan8pkw1dvPhcaVTb7CvjhJtjztF +YfDzAI5794qMlWxwilKMmUvDlPPOTen8NNHkLwWvyFCH7Doh +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEFjCCAv6gAwIBAgIJAMzYZJ+R9NBVMA0GCSqGSIb3DQEBCwUAMIGXMQswCQYD +VQQGEwJVUzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEi +MCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1h +em9uIFJEUzEoMCYGA1UEAwwfQW1hem9uIFJEUyBQcmV2aWV3IFJvb3QgMjAxOSBD +QTAeFw0xOTA4MjEyMjI5NDlaFw0yNDA4MjEyMjI5NDlaMIGXMQswCQYDVQQGEwJV +UzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEiMCAGA1UE +CgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJE +UzEoMCYGA1UEAwwfQW1hem9uIFJEUyBQcmV2aWV3IFJvb3QgMjAxOSBDQTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM7kkS6vjgKKQTPynC2NjdN5aPPV +O71G0JJS/2ARVBVJd93JLiGovVJilfWYfwZCs4gTRSSjrUD4D4HyqCd6A+eEEtJq +M0DEC7i0dC+9WNTsPszuB206Jy2IUmxZMIKJAA1NHSbIMjB+b6/JhbSUi7nKdbR/ +brj83bF+RoSA+ogrgX7mQbxhmFcoZN9OGaJgYKsKWUt5Wqv627KkGodUK8mDepgD +S3ZfoRQRx3iceETpcmHJvaIge6+vyDX3d9Z22jmvQ4AKv3py2CmU2UwuhOltFDwB +0ddtb39vgwrJxaGfiMRHpEP1DfNLWHAnA69/pgZPwIggidS+iBPUhgucMp8CAwEA +AaNjMGEwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FGnTGpQuQ2H/DZlXMQijZEhjs7TdMB8GA1UdIwQYMBaAFGnTGpQuQ2H/DZlXMQij +ZEhjs7TdMA0GCSqGSIb3DQEBCwUAA4IBAQC3xz1vQvcXAfpcZlngiRWeqU8zQAMQ +LZPCFNv7PVk4pmqX+ZiIRo4f9Zy7TrOVcboCnqmP/b/mNq0gVF4O+88jwXJZD+f8 +/RnABMZcnGU+vK0YmxsAtYU6TIb1uhRFmbF8K80HHbj9vSjBGIQdPCbvmR2zY6VJ +BYM+w9U9hp6H4DVMLKXPc1bFlKA5OBTgUtgkDibWJKFOEPW3UOYwp9uq6pFoN0AO +xMTldqWFsOF3bJIlvOY0c/1EFZXu3Ns6/oCP//Ap9vumldYMUZWmbK+gK33FPOXV +8BQ6jNC29icv7lLDpRPwjibJBXX+peDR5UK4FdYcswWEB1Tix5X8dYu6 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZUxCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSYwJAYDVQQDDB1BbWF6b24gUkRTIGFmLXNvdXRoLTEgUm9vdCBDQTAeFw0xOTEw +MjgxODA2NTNaFw0yNDEwMjgxODA2NTNaMIGQMQswCQYDVQQGEwJVUzETMBEGA1UE +CAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9u +IFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEhMB8GA1UE +AwwYQW1hem9uIFJEUyBhZi1zb3V0aC0xIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAvtV1OqmFa8zCVQSKOvPUJERLVFtd4rZmDpImc5rIoeBk7w/P +9lcKUJjO8R/w1a2lJXx3oQ81tiY0Piw6TpT62YWVRMWrOw8+Vxq1dNaDSFp9I8d0 +UHillSSbOk6FOrPDp+R6AwbGFqUDebbN5LFFoDKbhNmH1BVS0a6YNKpGigLRqhka +cClPslWtPqtjbaP3Jbxl26zWzLo7OtZl98dR225pq8aApNBwmtgA7Gh60HK/cX0t +32W94n8D+GKSg6R4MKredVFqRTi9hCCNUu0sxYPoELuM+mHiqB5NPjtm92EzCWs+ ++vgWhMc6GxG+82QSWx1Vj8sgLqtE/vLrWddf5QIDAQABo2YwZDAOBgNVHQ8BAf8E +BAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuLB4gYVJrSKJj/Gz +pqc6yeA+RcAwHwYDVR0jBBgwFoAUEEi/WWMcBJsoGXg+EZwkQ0MscZQwDQYJKoZI +hvcNAQELBQADggEBABauYOZxUhe9/RhzGJ8MsWCz8eKcyDVd4FCnY6Qh+9wcmYNT +LtnD88LACtJKb/b81qYzcB0Em6+zVJ3Z9jznfr6buItE6es9wAoja22Xgv44BTHL +rimbgMwpTt3uEMXDffaS0Ww6YWb3pSE0XYI2ISMWz+xRERRf+QqktSaL39zuiaW5 +tfZMre+YhohRa/F0ZQl3RCd6yFcLx4UoSPqQsUl97WhYzwAxZZfwvLJXOc4ATt3u +VlCUylNDkaZztDJc/yN5XQoK9W5nOt2cLu513MGYKbuarQr8f+gYU8S+qOyuSRSP +NRITzwCRVnsJE+2JmcRInn/NcanB7uOGqTvJ9+c= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZUxCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSYwJAYDVQQDDB1BbWF6b24gUkRTIGV1LXNvdXRoLTEgUm9vdCBDQTAeFw0xOTEw +MzAyMDIxMzBaFw0yNDEwMzAyMDIxMzBaMIGQMQswCQYDVQQGEwJVUzETMBEGA1UE +CAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9u +IFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEhMB8GA1UE +AwwYQW1hem9uIFJEUyBldS1zb3V0aC0xIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAtEyjYcajx6xImJn8Vz1zjdmL4ANPgQXwF7+tF7xccmNAZETb +bzb3I9i5fZlmrRaVznX+9biXVaGxYzIUIR3huQ3Q283KsDYnVuGa3mk690vhvJbB +QIPgKa5mVwJppnuJm78KqaSpi0vxyCPe3h8h6LLFawVyWrYNZ4okli1/U582eef8 +RzJp/Ear3KgHOLIiCdPDF0rjOdCG1MOlDLixVnPn9IYOciqO+VivXBg+jtfc5J+L +AaPm0/Yx4uELt1tkbWkm4BvTU/gBOODnYziITZM0l6Fgwvbwgq5duAtKW+h031lC +37rEvrclqcp4wrsUYcLAWX79ZyKIlRxcAdvEhQIDAQABo2YwZDAOBgNVHQ8BAf8E +BAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU7zPyc0azQxnBCe7D +b9KAadH1QSEwHwYDVR0jBBgwFoAUFBAFcgJe/BBuZiGeZ8STfpkgRYQwDQYJKoZI +hvcNAQELBQADggEBAFGaNiYxg7yC/xauXPlaqLCtwbm2dKyK9nIFbF/7be8mk7Q3 +MOA0of1vGHPLVQLr6bJJpD9MAbUcm4cPAwWaxwcNpxOjYOFDaq10PCK4eRAxZWwF +NJRIRmGsl8NEsMNTMCy8X+Kyw5EzH4vWFl5Uf2bGKOeFg0zt43jWQVOX6C+aL3Cd +pRS5MhmYpxMG8irrNOxf4NVFE2zpJOCm3bn0STLhkDcV/ww4zMzObTJhiIb5wSWn +EXKKWhUXuRt7A2y1KJtXpTbSRHQxE++69Go1tWhXtRiULCJtf7wF2Ksm0RR/AdXT +1uR1vKyH5KBJPX3ppYkQDukoHTFR0CpB+G84NLo= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZUxCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSYwJAYDVQQDDB1BbWF6b24gUkRTIG1lLXNvdXRoLTEgUm9vdCBDQTAeFw0xOTA1 +MTAyMTU4NDNaFw0yNTA2MDExMjAwMDBaMIGQMQswCQYDVQQGEwJVUzETMBEGA1UE +CAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9u +IFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEhMB8GA1UE +AwwYQW1hem9uIFJEUyBtZS1zb3V0aC0xIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAudOYPZH+ihJAo6hNYMB5izPVBe3TYhnZm8+X3IoaaYiKtsp1 +JJhkTT0CEejYIQ58Fh4QrMUyWvU8qsdK3diNyQRoYLbctsBPgxBR1u07eUJDv38/ +C1JlqgHmMnMi4y68Iy7ymv50QgAMuaBqgEBRI1R6Lfbyrb2YvH5txjJyTVMwuCfd +YPAtZVouRz0JxmnfsHyxjE+So56uOKTDuw++Ho4HhZ7Qveej7XB8b+PIPuroknd3 +FQB5RVbXRvt5ZcVD4F2fbEdBniF7FAF4dEiofVCQGQ2nynT7dZdEIPfPdH3n7ZmE +lAOmwHQ6G83OsiHRBLnbp+QZRgOsjkHJxT20bQIDAQABo2YwZDAOBgNVHQ8BAf8E +BAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUOEVDM7VomRH4HVdA +QvIMNq2tXOcwHwYDVR0jBBgwFoAU54cfDjgwBx4ycBH8+/r8WXdaiqYwDQYJKoZI +hvcNAQELBQADggEBAHhvMssj+Th8IpNePU6RH0BiL6o9c437R3Q4IEJeFdYL+nZz +PW/rELDPvLRUNMfKM+KzduLZ+l29HahxefejYPXtvXBlq/E/9czFDD4fWXg+zVou +uDXhyrV4kNmP4S0eqsAP/jQHPOZAMFA4yVwO9hlqmePhyDnszCh9c1PfJSBh49+b +4w7i/L3VBOMt8j3EKYvqz0gVfpeqhJwL4Hey8UbVfJRFJMJzfNHpePqtDRAY7yjV +PYquRaV2ab/E+/7VFkWMM4tazYz/qsYA2jSH+4xDHvYk8LnsbcrF9iuidQmEc5sb +FgcWaSKG4DJjcI5k7AJLWcXyTDt21Ci43LE+I9Q= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECDCCAvCgAwIBAgICVIYwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MDQxNzEz +MDRaFw0yNDA4MjIxNzA4NTBaMIGVMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEmMCQGA1UEAwwdQW1h +em9uIFJEUyBhcC1zb3V0aC0xIDIwMTkgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDUYOz1hGL42yUCrcsMSOoU8AeD/3KgZ4q7gP+vAz1WnY9K/kim +eWN/2Qqzlo3+mxSFQFyD4MyV3+CnCPnBl9Sh1G/F6kThNiJ7dEWSWBQGAB6HMDbC +BaAsmUc1UIz8sLTL3fO+S9wYhA63Wun0Fbm/Rn2yk/4WnJAaMZcEtYf6e0KNa0LM +p/kN/70/8cD3iz3dDR8zOZFpHoCtf0ek80QqTich0A9n3JLxR6g6tpwoYviVg89e +qCjQ4axxOkWWeusLeTJCcY6CkVyFvDAKvcUl1ytM5AiaUkXblE7zDFXRM4qMMRdt +lPm8d3pFxh0fRYk8bIKnpmtOpz3RIctDrZZxAgMBAAGjZjBkMA4GA1UdDwEB/wQE +AwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBT99wKJftD3jb4sHoHG +i3uGlH6W6TAfBgNVHSMEGDAWgBRzX2DYvMsDmPQrFzQuNlqmYP+8HzANBgkqhkiG +9w0BAQsFAAOCAQEAZ17hhr3dII3hUfuHQ1hPWGrpJOX/G9dLzkprEIcCidkmRYl+ +hu1Pe3caRMh/17+qsoEErmnVq5jNY9X1GZL04IZH8YbHc7iRHw3HcWAdhN8633+K +jYEB2LbJ3vluCGnCejq9djDb6alOugdLMJzxOkHDhMZ6/gYbECOot+ph1tQuZXzD +tZ7prRsrcuPBChHlPjmGy8M9z8u+kF196iNSUGC4lM8vLkHM7ycc1/ZOwRq9aaTe +iOghbQQyAEe03MWCyDGtSmDfr0qEk+CHN+6hPiaL8qKt4s+V9P7DeK4iW08ny8Ox +AVS7u0OK/5+jKMAMrKwpYrBydOjTUTHScocyNw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEBzCCAu+gAwIBAgICQ2QwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MDUxODQ2 +MjlaFw0yNDA4MjIxNzA4NTBaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzElMCMGA1UEAwwcQW1h +em9uIFJEUyBzYS1lYXN0LTEgMjAxOSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMMvR+ReRnOzqJzoaPipNTt1Z2VA968jlN1+SYKUrYM3No+Vpz0H +M6Tn0oYB66ByVsXiGc28ulsqX1HbHsxqDPwvQTKvO7SrmDokoAkjJgLocOLUAeld +5AwvUjxGRP6yY90NV7X786MpnYb2Il9DIIaV9HjCmPt+rjy2CZjS0UjPjCKNfB8J +bFjgW6GGscjeyGb/zFwcom5p4j0rLydbNaOr9wOyQrtt3ZQWLYGY9Zees/b8pmcc +Jt+7jstZ2UMV32OO/kIsJ4rMUn2r/uxccPwAc1IDeRSSxOrnFKhW3Cu69iB3bHp7 +JbawY12g7zshE4I14sHjv3QoXASoXjx4xgMCAwEAAaNmMGQwDgYDVR0PAQH/BAQD +AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFI1Fc/Ql2jx+oJPgBVYq +ccgP0pQ8MB8GA1UdIwQYMBaAFHNfYNi8ywOY9CsXNC42WqZg/7wfMA0GCSqGSIb3 +DQEBCwUAA4IBAQB4VVVabVp70myuYuZ3vltQIWqSUMhkaTzehMgGcHjMf9iLoZ/I +93KiFUSGnek5cRePyS9wcpp0fcBT3FvkjpUdCjVtdttJgZFhBxgTd8y26ImdDDMR +4+BUuhI5msvjL08f+Vkkpu1GQcGmyFVPFOy/UY8iefu+QyUuiBUnUuEDd49Hw0Fn +/kIPII6Vj82a2mWV/Q8e+rgN8dIRksRjKI03DEoP8lhPlsOkhdwU6Uz9Vu6NOB2Q +Ls1kbcxAc7cFSyRVJEhh12Sz9d0q/CQSTFsVJKOjSNQBQfVnLz1GwO/IieUEAr4C +jkTntH0r1LX5b/GwN4R887LvjAEdTbg1his7 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECDCCAvCgAwIBAgIDAIkHMA0GCSqGSIb3DQEBCwUAMIGPMQswCQYDVQQGEwJV +UzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEiMCAGA1UE +CgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJE +UzEgMB4GA1UEAwwXQW1hem9uIFJEUyBSb290IDIwMTkgQ0EwHhcNMTkwOTA2MTc0 +MDIxWhcNMjQwODIyMTcwODUwWjCBlDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldh +c2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoMGUFtYXpvbiBXZWIg +U2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxJTAjBgNVBAMMHEFt +YXpvbiBSRFMgdXMtd2VzdC0xIDIwMTkgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDD2yzbbAl77OofTghDMEf624OvU0eS9O+lsdO0QlbfUfWa1Kd6 +0WkgjkLZGfSRxEHMCnrv4UPBSK/Qwn6FTjkDLgemhqBtAnplN4VsoDL+BkRX4Wwq +/dSQJE2b+0hm9w9UMVGFDEq1TMotGGTD2B71eh9HEKzKhGzqiNeGsiX4VV+LJzdH +uM23eGisNqmd4iJV0zcAZ+Gbh2zK6fqTOCvXtm7Idccv8vZZnyk1FiWl3NR4WAgK +AkvWTIoFU3Mt7dIXKKClVmvssG8WHCkd3Xcb4FHy/G756UZcq67gMMTX/9fOFM/v +l5C0+CHl33Yig1vIDZd+fXV1KZD84dEJfEvHAgMBAAGjZjBkMA4GA1UdDwEB/wQE +AwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBR+ap20kO/6A7pPxo3+ +T3CfqZpQWjAfBgNVHSMEGDAWgBRzX2DYvMsDmPQrFzQuNlqmYP+8HzANBgkqhkiG +9w0BAQsFAAOCAQEAHCJky2tPjPttlDM/RIqExupBkNrnSYnOK4kr9xJ3sl8UF2DA +PAnYsjXp3rfcjN/k/FVOhxwzi3cXJF/2Tjj39Bm/OEfYTOJDNYtBwB0VVH4ffa/6 +tZl87jaIkrxJcreeeHqYMnIxeN0b/kliyA+a5L2Yb0VPjt9INq34QDc1v74FNZ17 +4z8nr1nzg4xsOWu0Dbjo966lm4nOYIGBRGOKEkHZRZ4mEiMgr3YLkv8gSmeitx57 +Z6dVemNtUic/LVo5Iqw4n3TBS0iF2C1Q1xT/s3h+0SXZlfOWttzSluDvoMv5PvCd +pFjNn+aXLAALoihL1MJSsxydtsLjOBro5eK0Vw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEDDCCAvSgAwIBAgICOFAwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTAxNzQ2 +MjFaFw0yNDA4MjIxNzA4NTBaMIGZMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEqMCgGA1UEAwwhQW1h +em9uIFJEUyBhcC1ub3J0aGVhc3QtMiAyMDE5IENBMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAzU72e6XbaJbi4HjJoRNjKxzUEuChKQIt7k3CWzNnmjc5 +8I1MjCpa2W1iw1BYVysXSNSsLOtUsfvBZxi/1uyMn5ZCaf9aeoA9UsSkFSZBjOCN +DpKPCmfV1zcEOvJz26+1m8WDg+8Oa60QV0ou2AU1tYcw98fOQjcAES0JXXB80P2s +3UfkNcnDz+l4k7j4SllhFPhH6BQ4lD2NiFAP4HwoG6FeJUn45EPjzrydxjq6v5Fc +cQ8rGuHADVXotDbEhaYhNjIrsPL+puhjWfhJjheEw8c4whRZNp6gJ/b6WEes/ZhZ +h32DwsDsZw0BfRDUMgUn8TdecNexHUw8vQWeC181hwIDAQABo2YwZDAOBgNVHQ8B +Af8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUwW9bWgkWkr0U +lrOsq2kvIdrECDgwHwYDVR0jBBgwFoAUc19g2LzLA5j0Kxc0LjZapmD/vB8wDQYJ +KoZIhvcNAQELBQADggEBAEugF0Gj7HVhX0ehPZoGRYRt3PBuI2YjfrrJRTZ9X5wc +9T8oHmw07mHmNy1qqWvooNJg09bDGfB0k5goC2emDiIiGfc/kvMLI7u+eQOoMKj6 +mkfCncyRN3ty08Po45vTLBFZGUvtQmjM6yKewc4sXiASSBmQUpsMbiHRCL72M5qV +obcJOjGcIdDTmV1BHdWT+XcjynsGjUqOvQWWhhLPrn4jWe6Xuxll75qlrpn3IrIx +CRBv/5r7qbcQJPOgwQsyK4kv9Ly8g7YT1/vYBlR3cRsYQjccw5ceWUj2DrMVWhJ4 +prf+E3Aa4vYmLLOUUvKnDQ1k3RGNu56V0tonsQbfsaM= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgICEzUwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTAyMDUy +MjVaFw0yNDA4MjIxNzA4NTBaMIGXMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEoMCYGA1UEAwwfQW1h +em9uIFJEUyBjYS1jZW50cmFsLTEgMjAxOSBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAOxHqdcPSA2uBjsCP4DLSlqSoPuQ/X1kkJLusVRKiQE2zayB +viuCBt4VB9Qsh2rW3iYGM+usDjltGnI1iUWA5KHcvHszSMkWAOYWLiMNKTlg6LCp +XnE89tvj5dIH6U8WlDvXLdjB/h30gW9JEX7S8supsBSci2GxEzb5mRdKaDuuF/0O +qvz4YE04pua3iZ9QwmMFuTAOYzD1M72aOpj+7Ac+YLMM61qOtU+AU6MndnQkKoQi +qmUN2A9IFaqHFzRlSdXwKCKUA4otzmz+/N3vFwjb5F4DSsbsrMfjeHMo6o/nb6Nh +YDb0VJxxPee6TxSuN7CQJ2FxMlFUezcoXqwqXD0CAwEAAaNmMGQwDgYDVR0PAQH/ +BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFDGGpon9WfIpsggE +CxHq8hZ7E2ESMB8GA1UdIwQYMBaAFHNfYNi8ywOY9CsXNC42WqZg/7wfMA0GCSqG +SIb3DQEBCwUAA4IBAQAvpeQYEGZvoTVLgV9rd2+StPYykMsmFjWQcyn3dBTZRXC2 +lKq7QhQczMAOhEaaN29ZprjQzsA2X/UauKzLR2Uyqc2qOeO9/YOl0H3qauo8C/W9 +r8xqPbOCDLEXlOQ19fidXyyEPHEq5WFp8j+fTh+s8WOx2M7IuC0ANEetIZURYhSp +xl9XOPRCJxOhj7JdelhpweX0BJDNHeUFi0ClnFOws8oKQ7sQEv66d5ddxqqZ3NVv +RbCvCtEutQMOUMIuaygDlMn1anSM8N7Wndx8G6+Uy67AnhjGx7jw/0YPPxopEj6x +JXP8j0sJbcT9K/9/fPVLNT25RvQ/93T2+IQL4Ca2 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEBzCCAu+gAwIBAgICYpgwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTExNzMx +NDhaFw0yNDA4MjIxNzA4NTBaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzElMCMGA1UEAwwcQW1h +em9uIFJEUyBldS13ZXN0LTEgMjAxOSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMk3YdSZ64iAYp6MyyKtYJtNzv7zFSnnNf6vv0FB4VnfITTMmOyZ +LXqKAT2ahZ00hXi34ewqJElgU6eUZT/QlzdIu359TEZyLVPwURflL6SWgdG01Q5X +O++7fSGcBRyIeuQWs9FJNIIqK8daF6qw0Rl5TXfu7P9dBc3zkgDXZm2DHmxGDD69 +7liQUiXzoE1q2Z9cA8+jirDioJxN9av8hQt12pskLQumhlArsMIhjhHRgF03HOh5 +tvi+RCfihVOxELyIRTRpTNiIwAqfZxxTWFTgfn+gijTmd0/1DseAe82aYic8JbuS +EMbrDduAWsqrnJ4GPzxHKLXX0JasCUcWyMECAwEAAaNmMGQwDgYDVR0PAQH/BAQD +AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFPLtsq1NrwJXO13C9eHt +sLY11AGwMB8GA1UdIwQYMBaAFHNfYNi8ywOY9CsXNC42WqZg/7wfMA0GCSqGSIb3 +DQEBCwUAA4IBAQAnWBKj5xV1A1mYd0kIgDdkjCwQkiKF5bjIbGkT3YEFFbXoJlSP +0lZZ/hDaOHI8wbLT44SzOvPEEmWF9EE7SJzkvSdQrUAWR9FwDLaU427ALI3ngNHy +lGJ2hse1fvSRNbmg8Sc9GBv8oqNIBPVuw+AJzHTacZ1OkyLZrz1c1QvwvwN2a+Jd +vH0V0YIhv66llKcYDMUQJAQi4+8nbRxXWv6Gq3pvrFoorzsnkr42V3JpbhnYiK+9 +nRKd4uWl62KRZjGkfMbmsqZpj2fdSWMY1UGyN1k+kDmCSWYdrTRDP0xjtIocwg+A +J116n4hV/5mbA0BaPiS2krtv17YAeHABZcvz +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgICV2YwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTExOTM2 +MjBaFw0yNDA4MjIxNzA4NTBaMIGXMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEoMCYGA1UEAwwfQW1h +em9uIFJEUyBldS1jZW50cmFsLTEgMjAxOSBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMEx54X2pHVv86APA0RWqxxRNmdkhAyp2R1cFWumKQRofoFv +n+SPXdkpIINpMuEIGJANozdiEz7SPsrAf8WHyD93j/ZxrdQftRcIGH41xasetKGl +I67uans8d+pgJgBKGb/Z+B5m+UsIuEVekpvgpwKtmmaLFC/NCGuSsJoFsRqoa6Gh +m34W6yJoY87UatddCqLY4IIXaBFsgK9Q/wYzYLbnWM6ZZvhJ52VMtdhcdzeTHNW0 +5LGuXJOF7Ahb4JkEhoo6TS2c0NxB4l4MBfBPgti+O7WjR3FfZHpt18A6Zkq6A2u6 +D/oTSL6c9/3sAaFTFgMyL3wHb2YlW0BPiljZIqECAwEAAaNmMGQwDgYDVR0PAQH/ +BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFOcAToAc6skWffJa +TnreaswAfrbcMB8GA1UdIwQYMBaAFHNfYNi8ywOY9CsXNC42WqZg/7wfMA0GCSqG +SIb3DQEBCwUAA4IBAQA1d0Whc1QtspK496mFWfFEQNegLh0a9GWYlJm+Htcj5Nxt +DAIGXb+8xrtOZFHmYP7VLCT5Zd2C+XytqseK/+s07iAr0/EPF+O2qcyQWMN5KhgE +cXw2SwuP9FPV3i+YAm11PBVeenrmzuk9NrdHQ7TxU4v7VGhcsd2C++0EisrmquWH +mgIfmVDGxphwoES52cY6t3fbnXmTkvENvR+h3rj+fUiSz0aSo+XZUGHPgvuEKM/W +CBD9Smc9CBoBgvy7BgHRgRUmwtABZHFUIEjHI5rIr7ZvYn+6A0O6sogRfvVYtWFc +qpyrW1YX8mD0VlJ8fGKM3G+aCOsiiPKDV/Uafrm+ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECDCCAvCgAwIBAgICGAcwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTIxODE5 +NDRaFw0yNDA4MjIxNzA4NTBaMIGVMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEmMCQGA1UEAwwdQW1h +em9uIFJEUyBldS1ub3J0aC0xIDIwMTkgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQCiIYnhe4UNBbdBb/nQxl5giM0XoVHWNrYV5nB0YukA98+TPn9v +Aoj1RGYmtryjhrf01Kuv8SWO+Eom95L3zquoTFcE2gmxCfk7bp6qJJ3eHOJB+QUO +XsNRh76fwDzEF1yTeZWH49oeL2xO13EAx4PbZuZpZBttBM5zAxgZkqu4uWQczFEs +JXfla7z2fvWmGcTagX10O5C18XaFroV0ubvSyIi75ue9ykg/nlFAeB7O0Wxae88e +uhiBEFAuLYdqWnsg3459NfV8Yi1GnaitTym6VI3tHKIFiUvkSiy0DAlAGV2iiyJE +q+DsVEO4/hSINJEtII4TMtysOsYPpINqeEzRAgMBAAGjZjBkMA4GA1UdDwEB/wQE +AwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRR0UpnbQyjnHChgmOc +hnlc0PogzTAfBgNVHSMEGDAWgBRzX2DYvMsDmPQrFzQuNlqmYP+8HzANBgkqhkiG +9w0BAQsFAAOCAQEAKJD4xVzSf4zSGTBJrmamo86jl1NHQxXUApAZuBZEc8tqC6TI +T5CeoSr9CMuVC8grYyBjXblC4OsM5NMvmsrXl/u5C9dEwtBFjo8mm53rOOIm1fxl +I1oYB/9mtO9ANWjkykuLzWeBlqDT/i7ckaKwalhLODsRDO73vRhYNjsIUGloNsKe +pxw3dzHwAZx4upSdEVG4RGCZ1D0LJ4Gw40OfD69hfkDfRVVxKGrbEzqxXRvovmDc +tKLdYZO/6REoca36v4BlgIs1CbUXJGLSXUwtg7YXGLSVBJ/U0+22iGJmBSNcoyUN +cjPFD9JQEhDDIYYKSGzIYpvslvGc4T5ISXFiuQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEBzCCAu+gAwIBAgICZIEwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTIyMTMy +MzJaFw0yNDA4MjIxNzA4NTBaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzElMCMGA1UEAwwcQW1h +em9uIFJEUyBldS13ZXN0LTIgMjAxOSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBALGiwqjiF7xIjT0Sx7zB3764K2T2a1DHnAxEOr+/EIftWKxWzT3u +PFwS2eEZcnKqSdRQ+vRzonLBeNLO4z8aLjQnNbkizZMBuXGm4BqRm1Kgq3nlLDQn +7YqdijOq54SpShvR/8zsO4sgMDMmHIYAJJOJqBdaus2smRt0NobIKc0liy7759KB +6kmQ47Gg+kfIwxrQA5zlvPLeQImxSoPi9LdbRoKvu7Iot7SOa+jGhVBh3VdqndJX +7tm/saj4NE375csmMETFLAOXjat7zViMRwVorX4V6AzEg1vkzxXpA9N7qywWIT5Y +fYaq5M8i6vvLg0CzrH9fHORtnkdjdu1y+0MCAwEAAaNmMGQwDgYDVR0PAQH/BAQD +AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFFOhOx1yt3Z7mvGB9jBv +2ymdZwiOMB8GA1UdIwQYMBaAFHNfYNi8ywOY9CsXNC42WqZg/7wfMA0GCSqGSIb3 +DQEBCwUAA4IBAQBehqY36UGDvPVU9+vtaYGr38dBbp+LzkjZzHwKT1XJSSUc2wqM +hnCIQKilonrTIvP1vmkQi8qHPvDRtBZKqvz/AErW/ZwQdZzqYNFd+BmOXaeZWV0Q +oHtDzXmcwtP8aUQpxN0e1xkWb1E80qoy+0uuRqb/50b/R4Q5qqSfJhkn6z8nwB10 +7RjLtJPrK8igxdpr3tGUzfAOyiPrIDncY7UJaL84GFp7WWAkH0WG3H8Y8DRcRXOU +mqDxDLUP3rNuow3jnGxiUY+gGX5OqaZg4f4P6QzOSmeQYs6nLpH0PiN00+oS1BbD +bpWdZEttILPI+vAYkU4QuBKKDjJL6HbSd+cn +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECDCCAvCgAwIBAgIDAIVCMA0GCSqGSIb3DQEBCwUAMIGPMQswCQYDVQQGEwJV +UzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEiMCAGA1UE +CgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJE +UzEgMB4GA1UEAwwXQW1hem9uIFJEUyBSb290IDIwMTkgQ0EwHhcNMTkwOTEzMTcw +NjQxWhcNMjQwODIyMTcwODUwWjCBlDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldh +c2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoMGUFtYXpvbiBXZWIg +U2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxJTAjBgNVBAMMHEFt +YXpvbiBSRFMgdXMtZWFzdC0yIDIwMTkgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDE+T2xYjUbxOp+pv+gRA3FO24+1zCWgXTDF1DHrh1lsPg5k7ht +2KPYzNc+Vg4E+jgPiW0BQnA6jStX5EqVh8BU60zELlxMNvpg4KumniMCZ3krtMUC +au1NF9rM7HBh+O+DYMBLK5eSIVt6lZosOb7bCi3V6wMLA8YqWSWqabkxwN4w0vXI +8lu5uXXFRemHnlNf+yA/4YtN4uaAyd0ami9+klwdkZfkrDOaiy59haOeBGL8EB/c +dbJJlguHH5CpCscs3RKtOOjEonXnKXldxarFdkMzi+aIIjQ8GyUOSAXHtQHb3gZ4 +nS6Ey0CMlwkB8vUObZU9fnjKJcL5QCQqOfwvAgMBAAGjZjBkMA4GA1UdDwEB/wQE +AwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBQUPuRHohPxx4VjykmH +6usGrLL1ETAfBgNVHSMEGDAWgBRzX2DYvMsDmPQrFzQuNlqmYP+8HzANBgkqhkiG +9w0BAQsFAAOCAQEAUdR9Vb3y33Yj6X6KGtuthZ08SwjImVQPtknzpajNE5jOJAh8 +quvQnU9nlnMO85fVDU1Dz3lLHGJ/YG1pt1Cqq2QQ200JcWCvBRgdvH6MjHoDQpqZ +HvQ3vLgOGqCLNQKFuet9BdpsHzsctKvCVaeBqbGpeCtt3Hh/26tgx0rorPLw90A2 +V8QSkZJjlcKkLa58N5CMM8Xz8KLWg3MZeT4DmlUXVCukqK2RGuP2L+aME8dOxqNv +OnOz1zrL5mR2iJoDpk8+VE/eBDmJX40IJk6jBjWoxAO/RXq+vBozuF5YHN1ujE92 +tO8HItgTp37XT8bJBAiAnt5mxw+NLSqtxk2QdQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEDDCCAvSgAwIBAgICY4kwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTMyMDEx +NDJaFw0yNDA4MjIxNzA4NTBaMIGZMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEqMCgGA1UEAwwhQW1h +em9uIFJEUyBhcC1zb3V0aGVhc3QtMSAyMDE5IENBMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAr5u9OuLL/OF/fBNUX2kINJLzFl4DnmrhnLuSeSnBPgbb +qddjf5EFFJBfv7IYiIWEFPDbDG5hoBwgMup5bZDbas+ZTJTotnnxVJTQ6wlhTmns +eHECcg2pqGIKGrxZfbQhlj08/4nNAPvyYCTS0bEcmQ1emuDPyvJBYDDLDU6AbCB5 +6Z7YKFQPTiCBblvvNzchjLWF9IpkqiTsPHiEt21sAdABxj9ityStV3ja/W9BfgxH +wzABSTAQT6FbDwmQMo7dcFOPRX+hewQSic2Rn1XYjmNYzgEHisdUsH7eeXREAcTw +61TRvaLH8AiOWBnTEJXPAe6wYfrcSd1pD0MXpoB62wIDAQABo2YwZDAOBgNVHQ8B +Af8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUytwMiomQOgX5 +Ichd+2lDWRUhkikwHwYDVR0jBBgwFoAUc19g2LzLA5j0Kxc0LjZapmD/vB8wDQYJ +KoZIhvcNAQELBQADggEBACf6lRDpfCD7BFRqiWM45hqIzffIaysmVfr+Jr+fBTjP +uYe/ba1omSrNGG23bOcT9LJ8hkQJ9d+FxUwYyICQNWOy6ejicm4z0C3VhphbTPqj +yjpt9nG56IAcV8BcRJh4o/2IfLNzC/dVuYJV8wj7XzwlvjysenwdrJCoLadkTr1h +eIdG6Le07sB9IxrGJL9e04afk37h7c8ESGSE4E+oS4JQEi3ATq8ne1B9DQ9SasXi +IRmhNAaISDzOPdyLXi9N9V9Lwe/DHcja7hgLGYx3UqfjhLhOKwp8HtoZORixAmOI +HfILgNmwyugAbuZoCazSKKBhQ0wgO0WZ66ZKTMG8Oho= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEBzCCAu+gAwIBAgICUYkwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTYxODIx +MTVaFw0yNDA4MjIxNzA4NTBaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzElMCMGA1UEAwwcQW1h +em9uIFJEUyB1cy13ZXN0LTIgMjAxOSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANCEZBZyu6yJQFZBJmSUZfSZd3Ui2gitczMKC4FLr0QzkbxY+cLa +uVONIOrPt4Rwi+3h/UdnUg917xao3S53XDf1TDMFEYp4U8EFPXqCn/GXBIWlU86P +PvBN+gzw3nS+aco7WXb+woTouvFVkk8FGU7J532llW8o/9ydQyDIMtdIkKTuMfho +OiNHSaNc+QXQ32TgvM9A/6q7ksUoNXGCP8hDOkSZ/YOLiI5TcdLh/aWj00ziL5bj +pvytiMZkilnc9dLY9QhRNr0vGqL0xjmWdoEXz9/OwjmCihHqJq+20MJPsvFm7D6a +2NKybR9U+ddrjb8/iyLOjURUZnj5O+2+OPcCAwEAAaNmMGQwDgYDVR0PAQH/BAQD +AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFEBxMBdv81xuzqcK5TVu +pHj+Aor8MB8GA1UdIwQYMBaAFHNfYNi8ywOY9CsXNC42WqZg/7wfMA0GCSqGSIb3 +DQEBCwUAA4IBAQBZkfiVqGoJjBI37aTlLOSjLcjI75L5wBrwO39q+B4cwcmpj58P +3sivv+jhYfAGEbQnGRzjuFoyPzWnZ1DesRExX+wrmHsLLQbF2kVjLZhEJMHF9eB7 +GZlTPdTzHErcnuXkwA/OqyXMpj9aghcQFuhCNguEfnROY9sAoK2PTfnTz9NJHL+Q +UpDLEJEUfc0GZMVWYhahc0x38ZnSY2SKacIPECQrTI0KpqZv/P+ijCEcMD9xmYEb +jL4en+XKS1uJpw5fIU5Sj0MxhdGstH6S84iAE5J3GM3XHklGSFwwqPYvuTXvANH6 +uboynxRgSae59jIlAK6Jrr6GWMwQRbgcaAlW +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEDDCCAvSgAwIBAgICEkYwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTYxOTUz +NDdaFw0yNDA4MjIxNzA4NTBaMIGZMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEqMCgGA1UEAwwhQW1h +em9uIFJEUyBhcC1zb3V0aGVhc3QtMiAyMDE5IENBMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAufodI2Flker8q7PXZG0P0vmFSlhQDw907A6eJuF/WeMo +GHnll3b4S6nC3oRS3nGeRMHbyU2KKXDwXNb3Mheu+ox+n5eb/BJ17eoj9HbQR1cd +gEkIciiAltf8gpMMQH4anP7TD+HNFlZnP7ii3geEJB2GGXSxgSWvUzH4etL67Zmn +TpGDWQMB0T8lK2ziLCMF4XAC/8xDELN/buHCNuhDpxpPebhct0T+f6Arzsiswt2j +7OeNeLLZwIZvVwAKF7zUFjC6m7/VmTQC8nidVY559D6l0UhhU0Co/txgq3HVsMOH +PbxmQUwJEKAzQXoIi+4uZzHFZrvov/nDTNJUhC6DqwIDAQABo2YwZDAOBgNVHQ8B +Af8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUwaZpaCme+EiV +M5gcjeHZSTgOn4owHwYDVR0jBBgwFoAUc19g2LzLA5j0Kxc0LjZapmD/vB8wDQYJ +KoZIhvcNAQELBQADggEBAAR6a2meCZuXO2TF9bGqKGtZmaah4pH2ETcEVUjkvXVz +sl+ZKbYjrun+VkcMGGKLUjS812e7eDF726ptoku9/PZZIxlJB0isC/0OyixI8N4M +NsEyvp52XN9QundTjkl362bomPnHAApeU0mRbMDRR2JdT70u6yAzGLGsUwMkoNnw +1VR4XKhXHYGWo7KMvFrZ1KcjWhubxLHxZWXRulPVtGmyWg/MvE6KF+2XMLhojhUL ++9jB3Fpn53s6KMx5tVq1x8PukHmowcZuAF8k+W4gk8Y68wIwynrdZrKRyRv6CVtR +FZ8DeJgoNZT3y/GT254VqMxxfuy2Ccb/RInd16tEvVk= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEDDCCAvSgAwIBAgICOYIwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTcyMDA1 +MjlaFw0yNDA4MjIxNzA4NTBaMIGZMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEqMCgGA1UEAwwhQW1h +em9uIFJEUyBhcC1ub3J0aGVhc3QtMyAyMDE5IENBMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA4dMak8W+XW8y/2F6nRiytFiA4XLwePadqWebGtlIgyCS +kbug8Jv5w7nlMkuxOxoUeD4WhI6A9EkAn3r0REM/2f0aYnd2KPxeqS2MrtdxxHw1 +xoOxk2x0piNSlOz6yog1idsKR5Wurf94fvM9FdTrMYPPrDabbGqiBMsZZmoHLvA3 +Z+57HEV2tU0Ei3vWeGIqnNjIekS+E06KhASxrkNU5vi611UsnYZlSi0VtJsH4UGV +LhnHl53aZL0YFO5mn/fzuNG/51qgk/6EFMMhaWInXX49Dia9FnnuWXwVwi6uX1Wn +7kjoHi5VtmC8ZlGEHroxX2DxEr6bhJTEpcLMnoQMqwIDAQABo2YwZDAOBgNVHQ8B +Af8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUsUI5Cb3SWB8+ +gv1YLN/ABPMdxSAwHwYDVR0jBBgwFoAUc19g2LzLA5j0Kxc0LjZapmD/vB8wDQYJ +KoZIhvcNAQELBQADggEBAJAF3E9PM1uzVL8YNdzb6fwJrxxqI2shvaMVmC1mXS+w +G0zh4v2hBZOf91l1EO0rwFD7+fxoI6hzQfMxIczh875T6vUXePKVOCOKI5wCrDad +zQbVqbFbdhsBjF4aUilOdtw2qjjs9JwPuB0VXN4/jY7m21oKEOcnpe36+7OiSPjN +xngYewCXKrSRqoj3mw+0w/+exYj3Wsush7uFssX18av78G+ehKPIVDXptOCP/N7W +8iKVNeQ2QGTnu2fzWsGUSvMGyM7yqT+h1ILaT//yQS8er511aHMLc142bD4D9VSy +DgactwPDTShK/PXqhvNey9v/sKXm4XatZvwcc8KYlW4= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEDDCCAvSgAwIBAgICcEUwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTgxNjU2 +MjBaFw0yNDA4MjIxNzA4NTBaMIGZMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEqMCgGA1UEAwwhQW1h +em9uIFJEUyBhcC1ub3J0aGVhc3QtMSAyMDE5IENBMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAndtkldmHtk4TVQAyqhAvtEHSMb6pLhyKrIFved1WO3S7 ++I+bWwv9b2W/ljJxLq9kdT43bhvzonNtI4a1LAohS6bqyirmk8sFfsWT3akb+4Sx +1sjc8Ovc9eqIWJCrUiSvv7+cS7ZTA9AgM1PxvHcsqrcUXiK3Jd/Dax9jdZE1e15s +BEhb2OEPE+tClFZ+soj8h8Pl2Clo5OAppEzYI4LmFKtp1X/BOf62k4jviXuCSst3 +UnRJzE/CXtjmN6oZySVWSe0rQYuyqRl6//9nK40cfGKyxVnimB8XrrcxUN743Vud +QQVU0Esm8OVTX013mXWQXJHP2c0aKkog8LOga0vobQIDAQABo2YwZDAOBgNVHQ8B +Af8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQULmoOS1mFSjj+ +snUPx4DgS3SkLFYwHwYDVR0jBBgwFoAUc19g2LzLA5j0Kxc0LjZapmD/vB8wDQYJ +KoZIhvcNAQELBQADggEBAAkVL2P1M2/G9GM3DANVAqYOwmX0Xk58YBHQu6iiQg4j +b4Ky/qsZIsgT7YBsZA4AOcPKQFgGTWhe9pvhmXqoN3RYltN8Vn7TbUm/ZVDoMsrM +gwv0+TKxW1/u7s8cXYfHPiTzVSJuOogHx99kBW6b2f99GbP7O1Sv3sLq4j6lVvBX +Fiacf5LAWC925nvlTzLlBgIc3O9xDtFeAGtZcEtxZJ4fnGXiqEnN4539+nqzIyYq +nvlgCzyvcfRAxwltrJHuuRu6Maw5AGcd2Y0saMhqOVq9KYKFKuD/927BTrbd2JVf +2sGWyuPZPCk3gq+5pCjbD0c6DkhcMGI6WwxvM5V/zSM= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEBzCCAu+gAwIBAgICJDQwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTgxNzAz +MTVaFw0yNDA4MjIxNzA4NTBaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzElMCMGA1UEAwwcQW1h +em9uIFJEUyBldS13ZXN0LTMgMjAxOSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL9bL7KE0n02DLVtlZ2PL+g/BuHpMYFq2JnE2RgompGurDIZdjmh +1pxfL3nT+QIVMubuAOy8InRfkRxfpxyjKYdfLJTPJG+jDVL+wDcPpACFVqoV7Prg +pVYEV0lc5aoYw4bSeYFhdzgim6F8iyjoPnObjll9mo4XsHzSoqJLCd0QC+VG9Fw2 +q+GDRZrLRmVM2oNGDRbGpGIFg77aRxRapFZa8SnUgs2AqzuzKiprVH5i0S0M6dWr +i+kk5epmTtkiDHceX+dP/0R1NcnkCPoQ9TglyXyPdUdTPPRfKCq12dftqll+u4mV +ARdN6WFjovxax8EAP2OAUTi1afY+1JFMj+sCAwEAAaNmMGQwDgYDVR0PAQH/BAQD +AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLfhrbrO5exkCVgxW0x3 +Y2mAi8lNMB8GA1UdIwQYMBaAFHNfYNi8ywOY9CsXNC42WqZg/7wfMA0GCSqGSIb3 +DQEBCwUAA4IBAQAigQ5VBNGyw+OZFXwxeJEAUYaXVoP/qrhTOJ6mCE2DXUVEoJeV +SxScy/TlFA9tJXqmit8JH8VQ/xDL4ubBfeMFAIAo4WzNWDVoeVMqphVEcDWBHsI1 +AETWzfsapRS9yQekOMmxg63d/nV8xewIl8aNVTHdHYXMqhhik47VrmaVEok1UQb3 +O971RadLXIEbVd9tjY5bMEHm89JsZDnDEw1hQXBb67Elu64OOxoKaHBgUH8AZn/2 +zFsL1ynNUjOhCSAA15pgd1vjwc0YsBbAEBPcHBWYBEyME6NLNarjOzBl4FMtATSF +wWCKRGkvqN8oxYhwR2jf2rR5Mu4DWkK5Q8Ep +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEBzCCAu+gAwIBAgICJVUwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSAwHgYDVQQDDBdBbWF6b24gUkRTIFJvb3QgMjAxOSBDQTAeFw0xOTA5MTkxODE2 +NTNaFw0yNDA4MjIxNzA4NTBaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2Fz +aGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBT +ZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzElMCMGA1UEAwwcQW1h +em9uIFJEUyB1cy1lYXN0LTEgMjAxOSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAM3i/k2u6cqbMdcISGRvh+m+L0yaSIoOXjtpNEoIftAipTUYoMhL +InXGlQBVA4shkekxp1N7HXe1Y/iMaPEyb3n+16pf3vdjKl7kaSkIhjdUz3oVUEYt +i8Z/XeJJ9H2aEGuiZh3kHixQcZczn8cg3dA9aeeyLSEnTkl/npzLf//669Ammyhs +XcAo58yvT0D4E0D/EEHf2N7HRX7j/TlyWvw/39SW0usiCrHPKDLxByLojxLdHzso +QIp/S04m+eWn6rmD+uUiRteN1hI5ncQiA3wo4G37mHnUEKo6TtTUh+sd/ku6a8HK +glMBcgqudDI90s1OpuIAWmuWpY//8xEG2YECAwEAAaNmMGQwDgYDVR0PAQH/BAQD +AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFPqhoWZcrVY9mU7tuemR +RBnQIj1jMB8GA1UdIwQYMBaAFHNfYNi8ywOY9CsXNC42WqZg/7wfMA0GCSqGSIb3 +DQEBCwUAA4IBAQB6zOLZ+YINEs72heHIWlPZ8c6WY8MDU+Be5w1M+BK2kpcVhCUK +PJO4nMXpgamEX8DIiaO7emsunwJzMSvavSPRnxXXTKIc0i/g1EbiDjnYX9d85DkC +E1LaAUCmCZBVi9fIe0H2r9whIh4uLWZA41oMnJx/MOmo3XyMfQoWcqaSFlMqfZM4 +0rNoB/tdHLNuV4eIdaw2mlHxdWDtF4oH+HFm+2cVBUVC1jXKrFv/euRVtsTT+A6i +h2XBHKxQ1Y4HgAn0jACP2QSPEmuoQEIa57bEKEcZsBR8SDY6ZdTd2HLRIApcCOSF +MRM8CKLeF658I0XgF8D5EsYoKPsA+74Z+jDH +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEETCCAvmgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSUwIwYDVQQDDBxBbWF6b24gUkRTIEJldGEgUm9vdCAyMDE5IENBMB4XDTE5MDgy +MDE3MTAwN1oXDTI0MDgxOTE3MzgyNlowgZkxCzAJBgNVBAYTAlVTMRMwEQYDVQQI +DApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMSIwIAYDVQQKDBlBbWF6b24g +V2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMSowKAYDVQQD +DCFBbWF6b24gUkRTIEJldGEgdXMtZWFzdC0xIDIwMTkgQ0EwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDTNCOlotQcLP8TP82U2+nk0bExVuuMVOgFeVMx +vbUHZQeIj9ikjk+jm6eTDnnkhoZcmJiJgRy+5Jt69QcRbb3y3SAU7VoHgtraVbxF +QDh7JEHI9tqEEVOA5OvRrDRcyeEYBoTDgh76ROco2lR+/9uCvGtHVrMCtG7BP7ZB +sSVNAr1IIRZZqKLv2skKT/7mzZR2ivcw9UeBBTUf8xsfiYVBvMGoEsXEycjYdf6w +WV+7XS7teNOc9UgsFNN+9AhIBc1jvee5E//72/4F8pAttAg/+mmPUyIKtekNJ4gj +OAR2VAzGx1ybzWPwIgOudZFHXFduxvq4f1hIRPH0KbQ/gkRrAgMBAAGjZjBkMA4G +A1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTkvpCD +6C43rar9TtJoXr7q8dkrrjAfBgNVHSMEGDAWgBStoQwVpbGx87fxB3dEGDqKKnBT +4TANBgkqhkiG9w0BAQsFAAOCAQEAJd9fOSkwB3uVdsS+puj6gCER8jqmhd3g/J5V +Zjk9cKS8H0e8pq/tMxeJ8kpurPAzUk5RkCspGt2l0BSwmf3ahr8aJRviMX6AuW3/ +g8aKplTvq/WMNGKLXONa3Sq8591J+ce8gtOX/1rDKmFI4wQ/gUzOSYiT991m7QKS +Fr6HMgFuz7RNJbb3Fy5cnurh8eYWA7mMv7laiLwTNsaro5qsqErD5uXuot6o9beT +a+GiKinEur35tNxAr47ax4IRubuIzyfCrezjfKc5raVV2NURJDyKP0m0CCaffAxE +qn2dNfYc3v1D8ypg3XjHlOzRo32RB04o8ALHMD9LSwsYDLpMag== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEFzCCAv+gAwIBAgICFSUwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVT +MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK +DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT +MSgwJgYDVQQDDB9BbWF6b24gUkRTIFByZXZpZXcgUm9vdCAyMDE5IENBMB4XDTE5 +MDgyMTIyMzk0N1oXDTI0MDgyMTIyMjk0OVowgZwxCzAJBgNVBAYTAlVTMRMwEQYD +VQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMSIwIAYDVQQKDBlBbWF6 +b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMS0wKwYD +VQQDDCRBbWF6b24gUkRTIFByZXZpZXcgdXMtZWFzdC0yIDIwMTkgQ0EwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0dB/U7qRnSf05wOi7m10Pa2uPMTJv +r6U/3Y17a5prq5Zr4++CnSUYarG51YuIf355dKs+7Lpzs782PIwCmLpzAHKWzix6 +pOaTQ+WZ0+vUMTxyqgqWbsBgSCyP7pVBiyqnmLC/L4az9XnscrbAX4pNaoJxsuQe +mzBo6yofjQaAzCX69DuqxFkVTRQnVy7LCFkVaZtjNAftnAHJjVgQw7lIhdGZp9q9 +IafRt2gteihYfpn+EAQ/t/E4MnhrYs4CPLfS7BaYXBycEKC5Muj1l4GijNNQ0Efo +xG8LSZz7SNgUvfVwiNTaqfLP3AtEAWiqxyMyh3VO+1HpCjT7uNBFtmF3AgMBAAGj +ZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW +BBQtinkdrj+0B2+qdXngV2tgHnPIujAfBgNVHSMEGDAWgBRp0xqULkNh/w2ZVzEI +o2RIY7O03TANBgkqhkiG9w0BAQsFAAOCAQEAtJdqbCxDeMc8VN1/RzCabw9BIL/z +73Auh8eFTww/sup26yn8NWUkfbckeDYr1BrXa+rPyLfHpg06kwR8rBKyrs5mHwJx +bvOzXD/5WTdgreB+2Fb7mXNvWhenYuji1MF+q1R2DXV3I05zWHteKX6Dajmx+Uuq +Yq78oaCBSV48hMxWlp8fm40ANCL1+gzQ122xweMFN09FmNYFhwuW+Ao+Vv90ZfQG +PYwTvN4n/gegw2TYcifGZC2PNX74q3DH03DXe5fvNgRW5plgz/7f+9mS+YHd5qa9 +tYTPUvoRbi169ou6jicsMKUKPORHWhiTpSCWR1FMMIbsAcsyrvtIsuaGCQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQdOCSuA9psBpQd8EI368/0DANBgkqhkiG9w0BAQsFADCB +lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB +bWF6b24gUkRTIHNhLWVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcM +B1NlYXR0bGUwIBcNMjEwNTE5MTgwNjI2WhgPMjA2MTA1MTkxOTA2MjZaMIGXMQsw +CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET +MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv +biBSRFMgc2EtZWFzdC0xIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UEBwwHU2Vh +dHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN6ftL6w8v3dB2yW +LjCxSP1D7ZsOTeLZOSCz1Zv0Gkd0XLhil5MdHOHBvwH/DrXqFU2oGzCRuAy+aZis +DardJU6ChyIQIciXCO37f0K23edhtpXuruTLLwUwzeEPdcnLPCX+sWEn9Y5FPnVm +pCd6J8edH2IfSGoa9LdErkpuESXdidLym/w0tWG/O2By4TabkNSmpdrCL00cqI+c +prA8Bx1jX8/9sY0gpAovtuFaRN+Ivg3PAnWuhqiSYyQ5nC2qDparOWuDiOhpY56E +EgmTvjwqMMjNtExfYx6Rv2Ndu50TriiNKEZBzEtkekwXInTupmYTvc7U83P/959V +UiQ+WSMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU4uYHdH0+ +bUeh81Eq2l5/RJbW+vswDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IB +AQBhxcExJ+w74bvDknrPZDRgTeMLYgbVJjx2ExH7/Ac5FZZWcpUpFwWMIJJxtewI +AnhryzM3tQYYd4CG9O+Iu0+h/VVfW7e4O3joWVkxNMb820kQSEwvZfA78aItGwOY +WSaFNVRyloVicZRNJSyb1UL9EiJ9ldhxm4LTT0ax+4ontI7zTx6n6h8Sr6r/UOvX +d9T5aUUENWeo6M9jGupHNn3BobtL7BZm2oS8wX8IVYj4tl0q5T89zDi2x0MxbsIV +5ZjwqBQ5JWKv7ASGPb+z286RjPA9R2knF4lJVZrYuNV90rHvI/ECyt/JrDqeljGL +BLl1W/UsvZo6ldLIpoMbbrb5 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEBDCCAuygAwIBAgIQUfVbqapkLYpUqcLajpTJWzANBgkqhkiG9w0BAQsFADCB +mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB +bWF6b24gUkRTIG1lLWNlbnRyYWwtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNV +BAcMB1NlYXR0bGUwIBcNMjIwNTA2MjMyMDA5WhgPMjA2MjA1MDcwMDIwMDlaMIGa +MQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5j +LjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMzAxBgNVBAMMKkFt +YXpvbiBSRFMgbWUtY2VudHJhbC0xIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UE +BwwHU2VhdHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJIeovu3 +ewI9FVitXMQzvkh34aQ6WyI4NO3YepfJaePiv3cnyFGYHN2S1cR3UQcLWgypP5va +j6bfroqwGbCbZZcb+6cyOB4ceKO9Ws1UkcaGHnNDcy5gXR7aCW2OGTUfinUuhd2d +5bOGgV7JsPbpw0bwJ156+MwfOK40OLCWVbzy8B1kITs4RUPNa/ZJnvIbiMu9rdj4 +8y7GSFJLnKCjlOFUkNI5LcaYvI1+ybuNgphT3nuu5ZirvTswGakGUT/Q0J3dxP0J +pDfg5Sj/2G4gXiaM0LppVOoU5yEwVewhQ250l0eQAqSrwPqAkdTg9ng360zqCFPE +JPPcgI1tdGUgneECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +/2AJVxWdZxc8eJgdpbwpW7b0f7IwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQBYm63jTu2qYKJ94gKnqc+oUgqmb1mTXmgmp/lXDbxonjszJDOXFbri +3CCO7xB2sg9bd5YWY8sGKHaWmENj3FZpCmoefbUx++8D7Mny95Cz8R32rNcwsPTl +ebpd9A/Oaw5ug6M0x/cNr0qzF8Wk9Dx+nFEimp8RYQdKvLDfNFZHjPa1itnTiD8M +TorAqj+VwnUGHOYBsT/0NY12tnwXdD+ATWfpEHdOXV+kTMqFFwDyhfgRVNpTc+os +ygr8SwhnSCpJPB/EYl2S7r+tgAbJOkuwUvGT4pTqrzDQEhwE7swgepnHC87zhf6l +qN6mVpSnQKQLm6Ob5TeCEFgcyElsF5bH +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjSgAwIBAgIRAOxu0I1QuMAhIeszB3fJIlkwCgYIKoZIzj0EAwMwgZYx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h +em9uIFJEUyB1cy13ZXN0LTIgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjEwNTI0MjIwNjU5WhgPMjEyMTA1MjQyMzA2NTlaMIGWMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS +RFMgdXMtd2VzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEz4bylRcGqqDWdP7gQIIoTHdBK6FNtKH1 +4SkEIXRXkYDmRvL9Bci1MuGrwuvrka5TDj4b7e+csY0llEzHpKfq6nJPFljoYYP9 +uqHFkv77nOpJJ633KOr8IxmeHW5RXgrZo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G +A1UdDgQWBBQQikVz8wmjd9eDFRXzBIU8OseiGzAOBgNVHQ8BAf8EBAMCAYYwCgYI +KoZIzj0EAwMDaAAwZQIwf06Mcrpw1O0EBLBBrp84m37NYtOkE/0Z0O+C7D41wnXi +EQdn6PXUVgdD23Gj82SrAjEAklhKs+liO1PtN15yeZR1Io98nFve+lLptaLakZcH ++hfFuUtCqMbaI8CdvJlKnPqT +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGCTCCA/GgAwIBAgIRALyWMTyCebLZOGcZZQmkmfcwDQYJKoZIhvcNAQEMBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1ub3J0aGVhc3QtMyBSb290IENBIFJTQTQwOTYgRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTI0MjAyODAzWhgPMjEyMTA1MjQyMTI4MDNa +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgYXAtbm9ydGhlYXN0LTMgUm9vdCBDQSBSU0E0MDk2IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +wGFiyDyCrGqgdn4fXG12cxKAAfVvhMea1mw5h9CVRoavkPqhzQpAitSOuMB9DeiP +wQyqcsiGl/cTEau4L+AUBG8b9v26RlY48exUYBXj8CieYntOT9iNw5WtdYJa3kF/ +JxgI+HDMzE9cmHDs5DOO3S0uwZVyra/xE1ymfSlpOeUIOTpHRJv97CBUEpaZMUW5 +Sr6GruuOwFVpO5FX3A/jQlcS+UN4GjSRgDUJuqg6RRQldEZGCVCCmodbByvI2fGm +reGpsPJD54KkmAX08nOR8e5hkGoHxq0m2DLD4SrOFmt65vG47qnuwplWJjtk9B3Z +9wDoopwZLBOtlkPIkUllWm1P8EuHC1IKOA+wSP6XdT7cy8S77wgyHzR0ynxv7q/l +vlZtH30wnNqFI0y9FeogD0TGMCHcnGqfBSicJXPy9T4fU6f0r1HwqKwPp2GArwe7 +dnqLTj2D7M9MyVtFjEs6gfGWXmu1y5uDrf+CszurE8Cycoma+OfjjuVQgWOCy7Nd +jJswPxAroTzVfpgoxXza4ShUY10woZu0/J+HmNmqK7lh4NS75q1tz75in8uTZDkV +be7GK+SEusTrRgcf3tlgPjSTWG3veNzFDF2Vn1GLJXmuZfhdlVQDBNXW4MNREExS +dG57kJjICpT+r8X+si+5j51gRzkSnMYs7VHulpxfcwECAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQU4JWOpDBmUBuWKvGPZelw87ezhL8wDgYDVR0P +AQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQBRNLMql7itvXSEFQRAnyOjivHz +l5IlWVQjAbOUr6ogZcwvK6YpxNAFW5zQr8F+fdkiypLz1kk5irx9TIpff0BWC9hQ +/odMPO8Gxn8+COlSvc+dLsF2Dax3Hvz0zLeKMo+cYisJOzpdR/eKd0/AmFdkvQoM +AOK9n0yYvVJU2IrSgeJBiiCarpKSeAktEVQ4rvyacQGr+QAPkkjRwm+5LHZKK43W +nNnggRli9N/27qYtc5bgr3AaQEhEXMI4RxPRXCLsod0ehMGWyRRK728a+6PMMJAJ +WHOU0x7LCEMPP/bvpLj3BdvSGqNor4ZtyXEbwREry1uzsgODeRRns5acPwTM6ff+ +CmxO2NZ0OktIUSYRmf6H/ZFlZrIhV8uWaIwEJDz71qvj7buhQ+RFDZ9CNL64C0X6 +mf0zJGEpddjANHaaVky+F4gYMtEy2K2Lcm4JGTdyIzUoIe+atzCnRp0QeIcuWtF+ +s8AjDYCVFNypcMmqbRmNpITSnOoCHSRuVkY3gutVoYyMLbp8Jm9SJnCIlEWTA6Rm +wADOMGZJVn5/XRTRuetVOB3KlQDjs9OO01XN5NzGSZO2KT9ngAUfh9Eqhf1iRWSP +nZlRbQ2NRCuY/oJ5N59mLGxnNJSE7giEKEBRhTQ/XEPIUYAUPD5fca0arKRJwbol +l9Se1Hsq0ZU5f+OZKQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +5zT5+Bs= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +Hing +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID/zCCAuegAwIBAgIRAI+asxQA/MB1cGyyrC0MPpkwDQYJKoZIhvcNAQELBQAw +gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn +QW1hem9uIFJEUyBjYS13ZXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIzMDkxMzIwMjEzNFoYDzIwNjMwOTEzMjEyMTMzWjCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIGNhLXdlc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl +YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDMHvQITTZcfl2O +yfzRIAPKwzzlc8eXWdXef7VUsbezg3lm9RC+vArO4JuAzta/aLw1D94wPSRm9JXX +NkP3obO6Ql80/0doooU6BAPceD0xmEWC4aCFT/5KWsD6Sy2/Rjwq3NKBTwzxLwYK +GqVsBp8AdrzDTmdRETC+Dg2czEo32mTDAA1uMgqrz6xxeTYroj8NTSTp6jfE6C0n +YgzYmVQCEIjHqI49j7k3jfT3P2skCVKGJwQzoZnerFacKzXsDB18uIqU7NaMc2cX +kOd0gRqpyKOzAHU2m5/S4jw4UHdkoI3E7nkayuen8ZPKH2YqWtTXUrXGhSTT34nX +yiFgu+vTAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHzz1NTd +TOm9zAv4d8l6XCFKSdJfMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC +AQEAodBvd0cvXQYhFBef2evnuI9XA+AC/Q9P1nYtbp5MPA4aFhy5v9rjW8wwJX14 +l+ltd2o3tz8PFDBZ1NX2ooiWVlZthQxKn1/xDVKsTXHbYUXItPQ3jI5IscB5IML8 +oCzAbkoLXsSPNOVFP5P4l4cZEMqHGRnBag7hLJZvmvzZSBnz+ioC2jpjVluF8kDX +fQGNjqPECik68CqbSV0SaQ0cgEoYTDjwON5ZLBeS8sxR2abE/gsj4VFYl5w/uEBd +w3Tt9uGfIy+wd2tNj6isGC6PcbPMjA31jd+ifs2yNzigqkcYTTWFtnvh4a8xiecm +GHu2EgH0Jqzz500N7L3uQdPkdg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIRANxgyBbnxgTEOpDul2ZnC0UwDQYJKoZIhvcNAQELBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtMyBSb290IENBIFJTQTIwNDggRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjEwNjEwMTgxOTA3WhgPMjA2MTA2MTAxOTE5MDda +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgYXAtc291dGhlYXN0LTMgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +xnwSDAChrMkfk5TA4Dk8hKzStDlSlONzmd3fTG0Wqr5+x3EmFT6Ksiu/WIwEl9J2 +K98UI7vYyuZfCxUKb1iMPeBdVGqk0zb92GpURd+Iz/+K1ps9ZLeGBkzR8mBmAi1S +OfpwKiTBzIv6E8twhEn4IUpHsdcuX/2Y78uESpJyM8O5CpkG0JaV9FNEbDkJeBUQ +Ao2qqNcH4R0Qcr5pyeqA9Zto1RswgL06BQMI9dTpfwSP5VvkvcNUaLl7Zv5WzLQE +JzORWePvdPzzvWEkY/3FPjxBypuYwssKaERW0fkPDmPtykktP9W/oJolKUFI6pXp +y+Y6p6/AVdnQD2zZjW5FhQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBT+jEKs96LC+/X4BZkUYUkzPfXdqTAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI +hvcNAQELBQADggEBAIGQqgqcQ6XSGkmNebzR6DhadTbfDmbYeN5N0Vuzv+Tdmufb +tMGjdjnYMg4B+IVnTKQb+Ox3pL9gbX6KglGK8HupobmIRtwKVth+gYYz3m0SL/Nk +haWPYzOm0x3tJm8jSdufJcEob4/ATce9JwseLl76pSWdl5A4lLjnhPPKudUDfH+1 +BLNUi3lxpp6GkC8aWUPtupnhZuXddolTLOuA3GwTZySI44NfaFRm+o83N1jp+EwD +6e94M4cTRzjUv6J3MZmSbdtQP/Tk1uz2K4bQZGP0PZC3bVpqiesdE/xr+wbu8uHr +cM1JXH0AmXf1yIkTgyWzmvt0k1/vgcw5ixAqvvE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEATCCAumgAwIBAgIRAMhw98EQU18mIji+unM2YH8wDQYJKoZIhvcNAQELBQAw +gZgxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwo +QW1hem9uIFJEUyBhcC1zb3V0aC0yIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UE +BwwHU2VhdHRsZTAgFw0yMjA2MDYyMTQyMjJaGA8yMDYyMDYwNjIyNDIyMlowgZgx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwoQW1h +em9uIFJEUyBhcC1zb3V0aC0yIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UEBwwH +U2VhdHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIeeRoLfTm+7 +vqm7ZlFSx+1/CGYHyYrOOryM4/Z3dqYVHFMgWTR7V3ziO8RZ6yUanrRcWVX3PZbF +AfX0KFE8OgLsXEZIX8odSrq86+/Th5eZOchB2fDBsUB7GuN2rvFBbM8lTI9ivVOU +lbuTnYyb55nOXN7TpmH2bK+z5c1y9RVC5iQsNAl6IJNvSN8VCqXh31eK5MlKB4DT ++Y3OivCrSGsjM+UR59uZmwuFB1h+icE+U0p9Ct3Mjq3MzSX5tQb6ElTNGlfmyGpW +Kh7GQ5XU1KaKNZXoJ37H53woNSlq56bpVrKI4uv7ATpdpFubOnSLtpsKlpLdR3sy +Ws245200pC8CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUp0ki +6+eWvsnBjQhMxwMW5pwn7DgwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUA +A4IBAQB2V8lv0aqbYQpj/bmVv/83QfE4vOxKCJAHv7DQ35cJsTyBdF+8pBczzi3t +3VNL5IUgW6WkyuUOWnE0eqAFOUVj0yTS1jSAtfl3vOOzGJZmWBbqm9BKEdu1D8O6 +sB8bnomwiab2tNDHPmUslpdDqdabbkWwNWzLJ97oGFZ7KNODMEPXWKWNxg33iHfS +/nlmnrTVI3XgaNK9qLZiUrxu9Yz5gxi/1K+sG9/Dajd32ZxjRwDipOLiZbiXQrsd +qzIMY4GcWf3g1gHL5mCTfk7dG22h/rhPyGV0svaDnsb+hOt6sv1McMN6Y3Ou0mtM +/UaAXojREmJmTSCNvs2aBny3/2sy +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjSgAwIBAgIRAMnRxsKLYscJV8Qv5pWbL7swCgYIKoZIzj0EAwMwgZYx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h +em9uIFJEUyBzYS1lYXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjEwNTE5MTgxNjAxWhgPMjEyMTA1MTkxOTE2MDFaMIGWMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS +RFMgc2EtZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEjFOCZgTNVKxLKhUxffiDEvTLFhrmIqdO +dKqVdgDoELEzIHWDdC+19aDPitbCYtBVHl65ITu/9pn6mMUl5hhUNtfZuc6A+Iw1 +sBe0v0qI3y9Q9HdQYrGgeHDh8M5P7E2ho0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G +A1UdDgQWBBS5L7/8M0TzoBZk39Ps7BkfTB4yJTAOBgNVHQ8BAf8EBAMCAYYwCgYI +KoZIzj0EAwMDaAAwZQIwI43O0NtWKTgnVv9z0LO5UMZYgSve7GvGTwqktZYCMObE +rUI4QerXM9D6JwLy09mqAjEAypfkdLyVWtaElVDUyHFkihAS1I1oUxaaDrynLNQK +Ou/Ay+ns+J+GyvyDUjBpVVW1 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +zDY= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIRAK5EYG3iHserxMqgg+0EFjgwDQYJKoZIhvcNAQELBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1ub3J0aGVhc3QtMyBSb290IENBIFJTQTIwNDggRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTI0MjAyMzE2WhgPMjA2MTA1MjQyMTIzMTZa +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgYXAtbm9ydGhlYXN0LTMgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +s1L6TtB84LGraLHVC+rGPhLBW2P0oN/91Rq3AnYwqDOuTom7agANwEjvLq7dSRG/ +sIfZsSV/ABTgArZ5sCmLjHFZAo8Kd45yA9byx20RcYtAG8IZl+q1Cri+s0XefzyO +U6mlfXZkVe6lzjlfXBkrlE/+5ifVbJK4dqOS1t9cWIpgKqv5fbE6Qbq4LVT+5/WM +Vd2BOljuBMGMzdZubqFKFq4mzTuIYfnBm7SmHlZfTdfBYPP1ScNuhpjuzw4n3NCR +EdU6dQv04Q6th4r7eiOCwbWI9LkmVbvBe3ylhH63lApC7MiiPYLlB13xBubVHVhV +q1NHoNTi+zA3MN9HWicRxQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBSuxoqm0/wjNiZLvqv+JlQwsDvTPDAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI +hvcNAQELBQADggEBAFfTK/j5kv90uIbM8VaFdVbr/6weKTwehafT0pAk1bfLVX+7 +uf8oHgYiyKTTl0DFQicXejghXTeyzwoEkWSR8c6XkhD5vYG3oESqmt/RGvvoxz11 +rHHy7yHYu7RIUc3VQG60c4qxXv/1mWySGwVwJrnuyNT9KZXPevu3jVaWOVHEILaK +HvzQ2YEcWBPmde/zEseO2QeeGF8FL45Q1d66wqIP4nNUd2pCjeTS5SpB0MMx7yi9 +ki1OH1pv8tOuIdimtZ7wkdB8+JSZoaJ81b8sRrydRwJyvB88rftuI3YB4WwGuONT +ZezUPsmaoK69B0RChB0ofDpAaviF9V3xOWvVZfo= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +1axqeKpI0F5SBUPsaVNYY2uNFg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECDCCAvCgAwIBAgIQCREfzzVyDTMcNME+gWnTCTANBgkqhkiG9w0BAQsFADCB +nDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTUwMwYDVQQDDCxB +bWF6b24gUkRTIGFwLXNvdXRoZWFzdC0yIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4G +A1UEBwwHU2VhdHRsZTAgFw0yMTA1MjQyMDQyMzNaGA8yMDYxMDUyNDIxNDIzM1ow +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtMiBSb290IENBIFJTQTIwNDggRzExEDAO +BgNVBAcMB1NlYXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDL +1MT6br3L/4Pq87DPXtcjlXN3cnbNk2YqRAZHJayStTz8VtsFcGPJOpk14geRVeVk +e9uKFHRbcyr/RM4owrJTj5X4qcEuATYZbo6ou/rW2kYzuWFZpFp7lqm0vasV4Z9F +fChlhwkNks0UbM3G+psCSMNSoF19ERunj7w2c4E62LwujkeYLvKGNepjnaH10TJL +2krpERd+ZQ4jIpObtRcMH++bTrvklc+ei8W9lqrVOJL+89v2piN3Ecdd389uphst +qQdb1BBVXbhUrtuGHgVf7zKqN1SkCoktoWxVuOprVWhSvr7akaWeq0UmlvbEsujU +vADqxGMcJFyCzxx3CkJjAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFFk8UJmlhoxFT3PP12PvhvazHjT4MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQsFAAOCAQEAfFtr2lGoWVXmWAsIo2NYre7kzL8Xb9Tx7desKxCCz5HOOvIr +8JMB1YK6A7IOvQsLJQ/f1UnKRh3X3mJZjKIywfrMSh0FiDf+rjcEzXxw2dGtUem4 +A+WMvIA3jwxnJ90OQj5rQ8bg3iPtE6eojzo9vWQGw/Vu48Dtw1DJo9210Lq/6hze +hPhNkFh8fMXNT7Q1Wz/TJqJElyAQGNOXhyGpHKeb0jHMMhsy5UNoW5hLeMS5ffao +TBFWEJ1gVfxIU9QRxSh+62m46JIg+dwDlWv8Aww14KgepspRbMqDuaM2cinoejv6 +t3dyOyHHrsOyv3ffZUKtQhQbQr+sUcL89lARsg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID/zCCAuegAwIBAgIRAIJLTMpzGNxqHZ4t+c1MlCIwDQYJKoZIhvcNAQELBQAw +gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn +QW1hem9uIFJEUyBhcC1lYXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUyNTIxMzAzM1oYDzIwNjEwNTI1MjIzMDMzWjCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIGFwLWVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl +YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtdHut0ZhJ9Nn2 +MpVafFcwHdoEzx06okmmhjJsNy4l9QYVeh0UUoek0SufRNMRF4d5ibzpgZol0Y92 +/qKWNe0jNxhEj6sXyHsHPeYtNBPuDMzThfbvsLK8z7pBP7vVyGPGuppqW/6m4ZBB +lcc9fsf7xpZ689iSgoyjiT6J5wlVgmCx8hFYc/uvcRtfd8jAHvheug7QJ3zZmIye +V4htOW+fRVWnBjf40Q+7uTv790UAqs0Zboj4Yil+hER0ibG62y1g71XcCyvcVpto +2/XW7Y9NCgMNqQ7fGN3wR1gjtSYPd7DO32LTzYhutyvfbpAZjsAHnoObmoljcgXI +QjfBcCFpAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJI3aWLg +CS5xqU5WYVaeT5s8lpO0MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC +AQEAUwATpJOcGVOs3hZAgJwznWOoTzOVJKfrqBum7lvkVH1vBwxBl9CahaKj3ZOt +YYp2qJzhDUWludL164DL4ZjS6eRedLRviyy5cRy0581l1MxPWTThs27z+lCC14RL +PJZNVYYdl7Jy9Q5NsQ0RBINUKYlRY6OqGDySWyuMPgno2GPbE8aynMdKP+f6G/uE +YHOf08gFDqTsbyfa70ztgVEJaRooVf5JJq4UQtpDvVswW2reT96qi6tXPKHN5qp3 +3wI0I1Mp4ePmiBKku2dwYzPfrJK/pQlvu0Gu5lKOQ65QdotwLAAoaFqrf9za1yYs +INUkHLWIxDds+4OHNYcerGp5Dw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGCTCCA/GgAwIBAgIRAIO6ldra1KZvNWJ0TA1ihXEwDQYJKoZIhvcNAQEMBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTIxMjE0NTA1WhgPMjEyMTA1MjEyMjQ1MDVa +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgYXAtc291dGhlYXN0LTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +sDN52Si9pFSyZ1ruh3xAN0nVqEs960o2IK5CPu/ZfshFmzAwnx/MM8EHt/jMeZtj +SM58LADAsNDL01ELpFZATjgZQ6xNAyXRXE7RiTRUvNkK7O3o2qAGbLnJq/UqF7Sw +LRnB8V6hYOv+2EjVnohtGCn9SUFGZtYDjWXsLd4ML4Zpxv0a5LK7oEC7AHzbUR7R +jsjkrXqSv7GE7bvhSOhMkmgxgj1F3J0b0jdQdtyyj109aO0ATUmIvf+Bzadg5AI2 +A9UA+TUcGeebhpHu8AP1Hf56XIlzPpaQv3ZJ4vzoLaVNUC7XKzAl1dlvCl7Klg/C +84qmbD/tjZ6GHtzpLKgg7kQEV7mRoXq8X4wDX2AFPPQl2fv+Kbe+JODqm5ZjGegm +uskABBi8IFv1hYx9jEulZPxC6uD/09W2+niFm3pirnlWS83BwVDTUBzF+CooUIMT +jhWkIIZGDDgMJTzouBHfoSJtS1KpUZi99m2WyVs21MNKHeWAbs+zmI6TO5iiMC+T +uB8spaOiHFO1573Fmeer4sy3YA6qVoqVl6jjTQqOdy3frAMbCkwH22/crV8YA+08 +hLeHXrMK+6XUvU+EtHAM3VzcrLbuYJUI2XJbzTj5g0Eb8I8JWsHvWHR5K7Z7gceR +78AzxQmoGEfV6KABNWKsgoCQnfb1BidDJIe3BsI0A6UCAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUABp0MlB14MSHgAcuNSOhs3MOlUcwDgYDVR0P +AQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQCv4CIOBSQi/QR9NxdRgVAG/pAh +tFJhV7OWb/wqwsNKFDtg6tTxwaahdCfWpGWId15OUe7G9LoPiKiwM9C92n0ZeHRz +4ewbrQVo7Eu1JI1wf0rnZJISL72hVYKmlvaWaacHhWxvsbKLrB7vt6Cknxa+S993 +Kf8i2Psw8j5886gaxhiUtzMTBwoDWak8ZaK7m3Y6C6hXQk08+3pnIornVSFJ9dlS +PAqt5UPwWmrEfF+0uIDORlT+cvrAwgSp7nUF1q8iasledycZ/BxFgQqzNwnkBDwQ +Z/aM52ArGsTzfMhkZRz9HIEhz1/0mJw8gZtDVQroD8778h8zsx2SrIz7eWQ6uWsD +QEeSWXpcheiUtEfzkDImjr2DLbwbA23c9LoexUD10nwohhoiQQg77LmvBVxeu7WU +E63JqaYUlOLOzEmNJp85zekIgR8UTkO7Gc+5BD7P4noYscI7pPOL5rP7YLg15ZFi +ega+G53NTckRXz4metsd8XFWloDjZJJq4FfD60VuxgXzoMNT9wpFTNSH42PR2s9L +I1vcl3w8yNccs9se2utM2nLsItZ3J0m/+QSRiw9hbrTYTcM9sXki0DtH2kyIOwYf +lOrGJDiYOIrXSQK36H0gQ+8omlrUTvUj4msvkXuQjlfgx6sgp2duOAfnGxE7uHnc +UhnJzzoe6M+LfGHkVQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICuDCCAj2gAwIBAgIQSAG6j2WHtWUUuLGJTPb1nTAKBggqhkjOPQQDAzCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIGFwLW5vcnRoZWFzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUyMDE2MzgyNloYDzIxMjEwNTIwMTczODI2WjCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIGFwLW5vcnRoZWFzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE2eqwU4FOzW8RV1W381Bd +olhDOrqoMqzWli21oDUt7y8OnXM/lmAuOS6sr8Nt61BLVbONdbr+jgCYw75KabrK +ZGg3siqvMOgabIKkKuXO14wtrGyGDt7dnKXg5ERGYOZlo0IwQDAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBS1Acp2WYxOcblv5ikZ3ZIbRCCW+zAOBgNVHQ8BAf8E +BAMCAYYwCgYIKoZIzj0EAwMDaQAwZgIxAJL84J08PBprxmsAKPTotBuVI3MyW1r8 +xQ0i8lgCQUf8GcmYjQ0jI4oZyv+TuYJAcwIxAP9Xpzq0Docxb+4N1qVhpiOfWt1O +FnemFiy9m1l+wv6p3riQMPV7mBVpklmijkIv3Q== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIRALZLcqCVIJ25maDPE3sbPCIwDQYJKoZIhvcNAQELBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTIxMjEzOTM5WhgPMjA2MTA1MjEyMjM5Mzla +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgYXAtc291dGhlYXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +ypKc+6FfGx6Gl6fQ78WYS29QoKgQiur58oxR3zltWeg5fqh9Z85K5S3UbRSTqWWu +Xcfnkz0/FS07qHX+nWAGU27JiQb4YYqhjZNOAq8q0+ptFHJ6V7lyOqXBq5xOzO8f ++0DlbJSsy7GEtJp7d7QCM3M5KVY9dENVZUKeJwa8PC5StvwPx4jcLeZRJC2rAVDG +SW7NAInbATvr9ssSh03JqjXb+HDyywiqoQ7EVLtmtXWimX+0b3/2vhqcH5jgcKC9 +IGFydrjPbv4kwMrKnm6XlPZ9L0/3FMzanXPGd64LQVy51SI4d5Xymn0Mw2kMX8s6 +Nf05OsWcDzJ1n6/Q1qHSxQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBRmaIc8eNwGP7i6P7AJrNQuK6OpFzAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI +hvcNAQELBQADggEBAIBeHfGwz3S2zwIUIpqEEI5/sMySDeS+3nJR+woWAHeO0C8i +BJdDh+kzzkP0JkWpr/4NWz84/IdYo1lqASd1Kopz9aT1+iROXaWr43CtbzjXb7/X +Zv7eZZFC8/lS5SROq42pPWl4ekbR0w8XGQElmHYcWS41LBfKeHCUwv83ATF0XQ6I +4t+9YSqZHzj4vvedrvcRInzmwWJaal9s7Z6GuwTGmnMsN3LkhZ+/GD6oW3pU/Pyh +EtWqffjsLhfcdCs3gG8x9BbkcJPH5aPAVkPn4wc8wuXg6xxb9YGsQuY930GWTYRf +schbgjsuqznW4HHakq4WNhs1UdTSTKkRdZz7FUQ= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIRAM2zAbhyckaqRim63b+Tib8wDQYJKoZIhvcNAQELBQAw +gZ8xCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE4MDYGA1UEAwwv +QW1hem9uIFJEUyBQcmV2aWV3IHVzLWVhc3QtMiBSb290IENBIFJTQTIwNDggRzEx +EDAOBgNVBAcMB1NlYXR0bGUwIBcNMjEwNTE4MjA0OTQ1WhgPMjA2MTA1MTgyMTQ5 +NDVaMIGfMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl +cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExODA2BgNV +BAMML0FtYXpvbiBSRFMgUHJldmlldyB1cy1lYXN0LTIgUm9vdCBDQSBSU0EyMDQ4 +IEcxMRAwDgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA1ybjQMH1MkbvfKsWJaCTXeCSN1SG5UYid+Twe+TjuSqaXWonyp4WRR5z +tlkqq+L2MWUeQQAX3S17ivo/t84mpZ3Rla0cx39SJtP3BiA2BwfUKRjhPwOjmk7j +3zrcJjV5k1vSeLNOfFFSlwyDiVyLAE61lO6onBx+cRjelu0egMGq6WyFVidTdCmT +Q9Zw3W6LTrnPvPmEyjHy2yCHzH3E50KSd/5k4MliV4QTujnxYexI2eR8F8YQC4m3 +DYjXt/MicbqA366SOoJA50JbgpuVv62+LSBu56FpzY12wubmDZsdn4lsfYKiWxUy +uc83a2fRXsJZ1d3whxrl20VFtLFHFQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBRC0ytKmDYbfz0Bz0Psd4lRQV3aNTAOBgNVHQ8BAf8EBAMCAYYw +DQYJKoZIhvcNAQELBQADggEBAGv8qZu4uaeoF6zsbumauz6ea6tdcWt+hGFuwGrb +tRbI85ucAmVSX06x59DJClsb4MPhL1XmqO3RxVMIVVfRwRHWOsZQPnXm8OYQ2sny +rYuFln1COOz1U/KflZjgJmxbn8x4lYiTPZRLarG0V/OsCmnLkQLPtEl/spMu8Un7 +r3K8SkbWN80gg17Q8EV5mnFwycUx9xsTAaFItuG0en9bGsMgMmy+ZsDmTRbL+lcX +Fq8r4LT4QjrFz0shrzCwuuM4GmcYtBSxlacl+HxYEtAs5k10tmzRf6OYlY33tGf6 +1tkYvKryxDPF/EDgGp/LiBwx6ixYMBfISoYASt4V/ylAlHA= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICtTCCAjqgAwIBAgIRAK9BSZU6nIe6jqfODmuVctYwCgYIKoZIzj0EAwMwgZkx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEyMDAGA1UEAwwpQW1h +em9uIFJEUyBjYS1jZW50cmFsLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcM +B1NlYXR0bGUwIBcNMjEwNTIxMjIxMzA5WhgPMjEyMTA1MjEyMzEzMDlaMIGZMQsw +CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET +MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMjAwBgNVBAMMKUFtYXpv +biBSRFMgY2EtY2VudHJhbC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdT +ZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEUkEERcgxneT5H+P+fERcbGmf +bVx+M7rNWtgWUr6w+OBENebQA9ozTkeSg4c4M+qdYSObFqjxITdYxT1z/nHz1gyx +OKAhLjWu+nkbRefqy3RwXaWT680uUaAP6ccnkZOMo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSN6fxlg0s5Wny08uRBYZcQ3TUoyzAOBgNVHQ8BAf8EBAMC +AYYwCgYIKoZIzj0EAwMDaQAwZgIxAORaz+MBVoFBTmZ93j2G2vYTwA6T5hWzBWrx +CrI54pKn5g6At56DBrkjrwZF5T1enAIxAJe/LZ9xpDkAdxDgGJFN8gZYLRWc0NRy +Rb4hihy5vj9L+w9uKc9VfEBIFuhT7Z3ljg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIQB/57HSuaqUkLaasdjxUdPjANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB +bWF6b24gUkRTIGFwLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUxOTE3NDAzNFoYDzIwNjEwNTE5MTg0MDM0WjCBmDEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6 +b24gUkRTIGFwLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT +ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtbkaoVsUS76o +TgLFmcnaB8cswBk1M3Bf4IVRcwWT3a1HeJSnaJUqWHCJ+u3ip/zGVOYl0gN1MgBb +MuQRIJiB95zGVcIa6HZtx00VezDTr3jgGWRHmRjNVCCHGmxOZWvJjsIE1xavT/1j +QYV/ph4EZEIZ/qPq7e3rHohJaHDe23Z7QM9kbyqp2hANG2JtU/iUhCxqgqUHNozV +Zd0l5K6KnltZQoBhhekKgyiHqdTrH8fWajYl5seD71bs0Axowb+Oh0rwmrws3Db2 +Dh+oc2PwREnjHeca9/1C6J2vhY+V0LGaJmnnIuOANrslx2+bgMlyhf9j0Bv8AwSi +dSWsobOhNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQb7vJT +VciLN72yJGhaRKLn6Krn2TAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggEBAAxEj8N9GslReAQnNOBpGl8SLgCMTejQ6AW/bapQvzxrZrfVOZOYwp/5oV0f +9S1jcGysDM+DrmfUJNzWxq2Y586R94WtpH4UpJDGqZp+FuOVJL313te4609kopzO +lDdmd+8z61+0Au93wB1rMiEfnIMkOEyt7D2eTFJfJRKNmnPrd8RjimRDlFgcLWJA +3E8wca67Lz/G0eAeLhRHIXv429y8RRXDtKNNz0wA2RwURWIxyPjn1fHjA9SPDkeW +E1Bq7gZj+tBnrqz+ra3yjZ2blss6Ds3/uRY6NYqseFTZWmQWT7FolZEnT9vMUitW +I0VynUbShVpGf6946e0vgaaKw20= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQGyUVTaVjYJvWhroVEiHPpDANBgkqhkiG9w0BAQsFADCB +lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB +bWF6b24gUkRTIHVzLXdlc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcM +B1NlYXR0bGUwIBcNMjEwNTE5MTkwNDA2WhgPMjA2MTA1MTkyMDA0MDZaMIGXMQsw +CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET +MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv +biBSRFMgdXMtd2VzdC0xIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UEBwwHU2Vh +dHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANhyXpJ0t4nigRDZ +EwNtFOem1rM1k8k5XmziHKDvDk831p7QsX9ZOxl/BT59Pu/P+6W6SvasIyKls1sW +FJIjFF+6xRQcpoE5L5evMgN/JXahpKGeQJPOX9UEXVW5B8yi+/dyUitFT7YK5LZA +MqWBN/LtHVPa8UmE88RCDLiKkqiv229tmwZtWT7nlMTTCqiAHMFcryZHx0pf9VPh +x/iPV8p2gBJnuPwcz7z1kRKNmJ8/cWaY+9w4q7AYlAMaq/rzEqDaN2XXevdpsYAK +TMMj2kji4x1oZO50+VPNfBl5ZgJc92qz1ocF95SAwMfOUsP8AIRZkf0CILJYlgzk +/6u6qZECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm5jfcS9o ++LwL517HpB6hG+PmpBswDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IB +AQAcQ6lsqxi63MtpGk9XK8mCxGRLCad51+MF6gcNz6i6PAqhPOoKCoFqdj4cEQTF +F8dCfa3pvfJhxV6RIh+t5FCk/y6bWT8Ls/fYKVo6FhHj57bcemWsw/Z0XnROdVfK +Yqbc7zvjCPmwPHEqYBhjU34NcY4UF9yPmlLOL8uO1JKXa3CAR0htIoW4Pbmo6sA4 +6P0co/clW+3zzsQ92yUCjYmRNeSbdXbPfz3K/RtFfZ8jMtriRGuO7KNxp8MqrUho +HK8O0mlSUxGXBZMNicfo7qY8FD21GIPH9w5fp5oiAl7lqFzt3E3sCLD3IiVJmxbf +fUwpGd1XZBBSdIxysRLM6j48 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrTCCAjOgAwIBAgIQU+PAILXGkpoTcpF200VD/jAKBggqhkjOPQQDAzCBljEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMS8wLQYDVQQDDCZBbWF6 +b24gUkRTIGFwLWVhc3QtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2Vh +dHRsZTAgFw0yMTA1MjUyMTQ1MTFaGA8yMTIxMDUyNTIyNDUxMVowgZYxCzAJBgNV +BAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYD +VQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1hem9uIFJE +UyBhcC1lYXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1NlYXR0bGUw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAT3tFKE8Kw1sGQAvNLlLhd8OcGhlc7MiW/s +NXm3pOiCT4vZpawKvHBzD76Kcv+ZZzHRxQEmG1/muDzZGlKR32h8AAj+NNO2Wy3d +CKTtYMiVF6Z2zjtuSkZQdjuQbe4eQ7qjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFAiSQOp16Vv0Ohpvqcbd2j5RmhYNMA4GA1UdDwEB/wQEAwIBhjAKBggq +hkjOPQQDAwNoADBlAjBVsi+5Ape0kOhMt/WFkANkslD4qXA5uqhrfAtH29Xzz2NV +tR7akiA771OaIGB/6xsCMQCZt2egCtbX7J0WkuZ2KivTh66jecJr5DHvAP4X2xtS +F/5pS+AUhcKTEGjI9jDH3ew= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICuDCCAj2gAwIBAgIQT5mGlavQzFHsB7hV6Mmy6TAKBggqhkjOPQQDAzCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIGFwLXNvdXRoZWFzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUyNDIwNTAxNVoYDzIxMjEwNTI0MjE1MDE1WjCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIGFwLXNvdXRoZWFzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEcm4BBBjYK7clwm0HJRWS +flt3iYwoJbIXiXn9c1y3E+Vb7bmuyKhS4eO8mwO4GefUcXObRfoHY2TZLhMJLVBQ +7MN2xDc0RtZNj07BbGD3VAIFRTDX0mH9UNYd0JQM3t/Oo0IwQDAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRrd5ITedfAwrGo4FA9UaDaGFK3rjAOBgNVHQ8BAf8E +BAMCAYYwCgYIKoZIzj0EAwMDaQAwZgIxAPBNqmVv1IIA3EZyQ6XuVf4gj79/DMO8 +bkicNS1EcBpUqbSuU4Zwt2BYc8c/t7KVOQIxAOHoWkoKZPiKyCxfMtJpCZySUG+n +sXgB/LOyWE5BJcXUfm+T1ckeNoWeUUMOLmnJjg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIRAJcDeinvdNrDQBeJ8+t38WQwDQYJKoZIhvcNAQELBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtNCBSb290IENBIFJTQTIwNDggRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjIwNTI1MTY0OTE2WhgPMjA2MjA1MjUxNzQ5MTZa +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgYXAtc291dGhlYXN0LTQgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +k8DBNkr9tMoIM0NHoFiO7cQfSX0cOMhEuk/CHt0fFx95IBytx7GHCnNzpM27O5z6 +x6iRhfNnx+B6CrGyCzOjxvPizneY+h+9zfvNz9jj7L1I2uYMuiNyOKR6FkHR46CT +1CiArfVLLPaTqgD/rQjS0GL2sLHS/0dmYipzynnZcs613XT0rAWdYDYgxDq7r/Yi +Xge5AkWQFkMUq3nOYDLCyGGfQqWKkwv6lZUHLCDKf+Y0Uvsrj8YGCI1O8mF0qPCQ +lmlfaDvbuBu1AV+aabmkvyFj3b8KRIlNLEtQ4N8KGYR2Jdb82S4YUGIOAt4wuuFt +1B7AUDLk3V/u+HTWiwfoLQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBSNpcjz6ArWBtAA+Gz6kyyZxrrgdDAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI +hvcNAQELBQADggEBAGJEd7UgOzHYIcQRSF7nSYyjLROyalaIV9AX4WXW/Cqlul1c +MblP5etDZm7A/thliZIWAuyqv2bNicmS3xKvNy6/QYi1YgxZyy/qwJ3NdFl067W0 +t8nGo29B+EVK94IPjzFHWShuoktIgp+dmpijB7wkTIk8SmIoe9yuY4+hzgqk+bo4 +ms2SOXSN1DoQ75Xv+YmztbnZM8MuWhL1T7hA4AMorzTQLJ9Pof8SpSdMHeDsHp0R +01jogNFkwy25nw7cL62nufSuH2fPYGWXyNDg+y42wKsKWYXLRgUQuDVEJ2OmTFMB +T0Vf7VuNijfIA9hkN2d3K53m/9z5WjGPSdOjGhg= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQRiwspKyrO0xoxDgSkqLZczANBgkqhkiG9w0BAQsFADCB +lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB +bWF6b24gUkRTIHVzLXdlc3QtMiBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcM +B1NlYXR0bGUwIBcNMjEwNTI0MjE1OTAwWhgPMjA2MTA1MjQyMjU5MDBaMIGXMQsw +CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET +MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv +biBSRFMgdXMtd2VzdC0yIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UEBwwHU2Vh +dHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL53Jk3GsKiu+4bx +jDfsevWbwPCNJ3H08Zp7GWhvI3Tgi39opfHYv2ku2BKFjK8N2L6RvNPSR8yplv5j +Y0tK0U+XVNl8o0ibhqRDhbTuh6KL8CFINWYzAajuxFS+CF0U6c1Q3tXLBdALxA7l +FlXJ71QrP06W31kRe7kvgrvO7qWU3/OzUf9qYw4LSiR1/VkvvRCTqcVNw09clw/M +Jbw6FSgweN65M9j7zPbjGAXSHkXyxH1Erin2fa+B9PE4ZDgX9cp2C1DHewYJQL/g +SepwwcudVNRN1ibKH7kpMrgPnaNIVNx5sXVsTjk6q2ZqYw3SVHegltJpLy/cZReP +mlivF2kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUmTcQd6o1 +CuS65MjBrMwQ9JJjmBwwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IB +AQAKSDSIzl956wVddPThf2VAzI8syw9ngSwsEHZvxVGHBvu5gg618rDyguVCYX9L +4Kw/xJrk6S3qxOS2ZDyBcOpsrBskgahDFIunzoRP3a18ARQVq55LVgfwSDQiunch +Bd05cnFGLoiLkR5rrkgYaP2ftn3gRBRaf0y0S3JXZ2XB3sMZxGxavYq9mfiEcwB0 +LMTMQ1NYzahIeG6Jm3LqRqR8HkzP/Ztq4dT2AtSLvFebbNMiWqeqT7OcYp94HTYT +zqrtaVdUg9bwyAUCDgy0GV9RHDIdNAOInU/4LEETovrtuBU7Z1q4tcHXvN6Hd1H8 +gMb0mCG5I393qW5hFsA/diFb +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIRAPQAvihfjBg/JDbj6U64K98wDQYJKoZIhvcNAQELBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1ub3J0aGVhc3QtMiBSb290IENBIFJTQTIwNDggRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTIwMTYyODQxWhgPMjA2MTA1MjAxNzI4NDFa +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgYXAtbm9ydGhlYXN0LTIgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +vJ9lgyksCxkBlY40qOzI1TCj/Q0FVGuPL/Z1Mw2YN0l+41BDv0FHApjTUkIKOeIP +nwDwpXTa3NjYbk3cOZ/fpH2rYJ++Fte6PNDGPgKppVCUh6x3jiVZ1L7wOgnTdK1Q +Trw8440IDS5eLykRHvz8OmwvYDl0iIrt832V0QyOlHTGt6ZJ/aTQKl12Fy3QBLv7 +stClPzvHTrgWqVU6uidSYoDtzHbU7Vda7YH0wD9IUoMBf7Tu0rqcE4uH47s2XYkc +SdLEoOg/Ngs7Y9B1y1GCyj3Ux7hnyvCoRTw014QyNB7dTatFMDvYlrRDGG14KeiU +UL7Vo/+EejWI31eXNLw84wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBQkgTWFsNg6wA3HbbihDQ4vpt1E2zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI +hvcNAQELBQADggEBAGz1Asiw7hn5WYUj8RpOCzpE0h/oBZcnxP8wulzZ5Xd0YxWO +0jYUcUk3tTQy1QvoY+Q5aCjg6vFv+oFBAxkib/SmZzp4xLisZIGlzpJQuAgRkwWA +6BVMgRS+AaOMQ6wKPgz1x4v6T0cIELZEPq3piGxvvqkcLZKdCaeC3wCS6sxuafzZ +4qA3zMwWuLOzRftgX2hQto7d/2YkRXga7jSvQl3id/EI+xrYoH6zIWgjdU1AUaNq +NGT7DIo47vVMfnd9HFZNhREsd4GJE83I+JhTqIxiKPNxrKgESzyADmNPt0gXDnHo +tbV1pMZz5HpJtjnP/qVZhEK5oB0tqlKPv9yx074= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICuTCCAj6gAwIBAgIRAKp1Rn3aL/g/6oiHVIXtCq8wCgYIKoZIzj0EAwMwgZsx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE0MDIGA1UEAwwrQW1h +em9uIFJEUyBhcC1ub3J0aGVhc3QtMyBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UE +BwwHU2VhdHRsZTAgFw0yMTA1MjQyMDMyMTdaGA8yMTIxMDUyNDIxMzIxN1owgZsx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE0MDIGA1UEAwwrQW1h +em9uIFJEUyBhcC1ub3J0aGVhc3QtMyBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UE +BwwHU2VhdHRsZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABGTYWPILeBJXfcL3Dz4z +EWMUq78xB1HpjBwHoTURYfcMd5r96BTVG6yaUBWnAVCMeeD6yTG9a1eVGNhG14Hk +ZAEjgLiNB7RRbEG5JZ/XV7W/vODh09WCst2y9SLKsdgeAaNCMEAwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUoE0qZHmDCDB+Bnm8GUa/evpfPwgwDgYDVR0PAQH/ +BAQDAgGGMAoGCCqGSM49BAMDA2kAMGYCMQCnil5MMwhY3qoXv0xvcKZGxGPaBV15 +0CCssCKn0oVtdJQfJQ3Jrf3RSaEyijXIJsoCMQC35iJi4cWoNX3N/qfgnHohW52O +B5dg0DYMqy5cNZ40+UcAanRMyqNQ6P7fy3umGco= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICtzCCAj2gAwIBAgIQPXnDTPegvJrI98qz8WxrMjAKBggqhkjOPQQDAzCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIEJldGEgdXMtZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUxODIxNDAxMloYDzIxMjEwNTE4MjI0MDEyWjCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIEJldGEgdXMtZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEI0sR7gwutK5AB46hM761 +gcLTGBIYlURSEoM1jcBwy56CL+3CJKZwLLyJ7qoOKfWbu5GsVLUTWS8MV6Nw33cx +2KQD2svb694wi+Px2f4n9+XHkEFQw8BbiodDD7RZA70fo0IwQDAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTQSioOvnVLEMXwNSDg+zgln/vAkjAOBgNVHQ8BAf8E +BAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIxAMwu1hqm5Bc98uE/E0B5iMYbBQ4kpMxO +tP8FTfz5UR37HUn26nXE0puj6S/Ffj4oJgIwXI7s2c26tFQeqzq6u3lrNJHp5jC9 +Uxlo/hEJOLoDj5jnpxo8dMAtCNoQPaHdfL0P +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF/jCCA+agAwIBAgIQEM1pS+bWfBJeu/6j1yIIFzANBgkqhkiG9w0BAQwFADCB +lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB +bWF6b24gUkRTIGNhLXdlc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcM +B1NlYXR0bGUwIBcNMjMwOTE5MjIwMTM5WhgPMjEyMzA5MTkyMzAxMzlaMIGXMQsw +CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET +MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv +biBSRFMgY2Etd2VzdC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwHU2Vh +dHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Pyp8p5z6HnlGB +daOj78gZ3ABufxnBFiu5NdFiGoMrS+eY//xxr2iKbnynJAzjmn5A6VKMNxtbuYIZ +WKAzDb/HrWlIYD2w7ZVBXpylfPhiz3jLNsl03WdPNnEruCcivhY2QMewEVtzjPU0 +ofdbZlO2KpF3biv1gjPuIuE7AUyQAbWnWTlrzETAVWLboJJRRqxASSkFUHNLXod7 +ow02FwlAhcnCp9gSe1SKRDrpvvEvYQBAFB7owfnoQzOGDdd87RGyYfyuW8aFI2Z0 +LHNvsA0dTafO4Rh986c72kDL7ijICQdr5OTgZR2OnuESLk1DSK4xYJ4fA6jb5dJ5 ++xsI6tCPykWCW98aO/pha35OsrVNifL/5cH5pdv/ecgQGdffJB+Vdj6f/ZMwR6s/ +Rm37cQ9l3tU8eu/qpzsFjLq1ZUzDaVDWgMW9t49+q/zjhdmbPOabZDao7nHXrVRw +rwPHWCmEY4OmH6ikEKQW3AChFjOdSg4me/J0Jr5l5jKggLPHWbNLRO8qTTK6N8qk +ui3aJDi+XQfsTPARXIw4UFErArNImTsoZVyqfX7I4shp0qZbEhP6kRAbfPljw5kW +Yat7ZlXqDanjsreqbLTaOU10P0rC0/4Ctv5cLSKCrzRLWtpXxhKa2wJTQ74G6fAZ +1oUA79qg3F8nyM+ZzDsfNI854+PNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8w +HQYDVR0OBBYEFLRWiDabEQZNkzEPUCr1ZVJV6xpwMA4GA1UdDwEB/wQEAwIBhjAN +BgkqhkiG9w0BAQwFAAOCAgEATkVVzkkGBjEtLGDtERi+fSpIV0MxwAsA4PAeBBmb +myxo90jz6kWkKM1Wm4BkZM8/mq5VbxPef1kxHfb5CHksCL6SgG5KujfIvht+KT2a +MRJB+III3CbcTy0HtwCX5AlPIbXWydhQFoJTW/OkpecUWoyFM6SqYeYZx1itJpxl +sXshLjYOvw+QgvxRsDxqUfkcaC/N2yhu/30Zo2P8msJfAFry2UmA/TBrWOQKVQxl +Ee/yWgp4U/bC/GZnjWnWDTwkRFGQtI4wjxbVuX6V4FTLCT7kIoHBhG+zOSduJRn3 +Axej7gkEXEVc/PAnwp/kSJ/b0/JONLWdjGUFkyiMn1yJlhJ2sg39vepBN5r6yVYU +nJWoZAuupRpoIKfmC3/cZanXqYbYl4yxzX/PMB4kAACfdxGxLawjnnBjSzaWokXs +YVh2TjWpUMwLOi0RB2mtPUjHdDLKtjOTZ1zHZnR/wVp9BmVI1BXYnz5PAqU5XqeD +EmanyaAuFCeyol1EtbQhgtysThQ+vwYAXMm2iKzJxq0hik8wyG8X55FhnGEOGV3u +xxq7odd3/8BXkc3dGdBPQtH+k5glaQyPnAsLVAIUvyzTmy58saL+nJnQY4mmRrwV +1jJA7nnkaklI/L5fvfCg0W+TMinCOAGd+GQ4hK2SAsJLtcqiBgPf2wJHO8wiwUh9 +Luw= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQGKVv+5VuzEZEBzJ+bVfx2zAKBggqhkjOPQQDAzCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIGFwLXNvdXRoLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjEwNTE5MTc1MDU5WhgPMjEyMTA1MTkxODUwNTlaMIGXMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS +RFMgYXAtc291dGgtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs +ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMqdLJ0tZF/DGFZTKZDrGRJZID8ivC2I +JRCYTWweZKCKSCAzoiuGGHzJhr5RlLHQf/QgmFcgXsdmO2n3CggzhA4tOD9Ip7Lk +P05eHd2UPInyPCHRgmGjGb0Z+RdQ6zkitKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUC1yhRgVqU5bR8cGzOUCIxRpl4EYwDgYDVR0PAQH/BAQDAgGGMAoG +CCqGSM49BAMDA2cAMGQCMG0c/zLGECRPzGKJvYCkpFTCUvdP4J74YP0v/dPvKojL +t/BrR1Tg4xlfhaib7hPc7wIwFvgqHes20CubQnZmswbTKLUrgSUW4/lcKFpouFd2 +t2/ewfi/0VhkeUW+IiHhOMdU +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGCTCCA/GgAwIBAgIRAOXxJuyXVkbfhZCkS/dOpfEwDQYJKoZIhvcNAQEMBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1ub3J0aGVhc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTI1MjE1OTEwWhgPMjEyMTA1MjUyMjU5MTBa +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgYXAtbm9ydGhlYXN0LTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +xiP4RDYm4tIS12hGgn1csfO8onQDmK5SZDswUpl0HIKXOUVVWkHNlINkVxbdqpqH +FhbyZmNN6F/EWopotMDKe1B+NLrjNQf4zefv2vyKvPHJXhxoKmfyuTd5Wk8k1F7I +lNwLQzznB+ElhrLIDJl9Ro8t31YBBNFRGAGEnxyACFGcdkjlsa52UwfYrwreEg2l +gW5AzqHgjFfj9QRLydeU/n4bHm0F1adMsV7P3rVwilcUlqsENDwXnWyPEyv3sw6F +wNemLEs1129mB77fwvySb+lLNGsnzr8w4wdioZ74co+T9z2ca+eUiP+EQccVw1Is +D4Fh57IjPa6Wuc4mwiUYKkKY63+38aCfEWb0Qoi+zW+mE9nek6MOQ914cN12u5LX +dBoYopphRO5YmubSN4xcBy405nIdSdbrAVWwxXnVVyjqjknmNeqQsPZaxAhdoKhV +AqxNr8AUAdOAO6Sz3MslmcLlDXFihrEEOeUbpg/m1mSUUHGbu966ajTG1FuEHHwS +7WB52yxoJo/tHvt9nAWnh3uH5BHmS8zn6s6CGweWKbX5yICnZ1QFR1e4pogxX39v +XD6YcNOO+Vn+HY4nXmjgSYVC7l+eeP8eduMg1xJujzjrbmrXU+d+cBObgdTOAlpa +JFHaGwYw1osAwPCo9cZ2f04yitBfj9aPFia8ASKldakCAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUqKS+ltlior0SyZKYAkJ/efv55towDgYDVR0P +AQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQAdElvp8bW4B+Cv+1WSN87dg6TN +wGyIjJ14/QYURgyrZiYpUmZpj+/pJmprSWXu4KNyqHftmaidu7cdjL5nCAvAfnY5 +/6eDDbX4j8Gt9fb/6H9y0O0dn3mUPSEKG0crR+JRFAtPhn/2FNvst2P82yguWLv0 +pHjHVUVcq+HqDMtUIJsTPYjSh9Iy77Q6TOZKln9dyDOWJpCSkiUWQtMAKbCSlvzd +zTs/ahqpT+zLfGR1SR+T3snZHgQnbnemmz/XtlKl52NxccARwfcEEKaCRQyGq/pR +0PVZasyJS9JY4JfQs4YOdeOt4UMZ8BmW1+BQWGSkkb0QIRl8CszoKofucAlqdPcO +IT/ZaMVhI580LFGWiQIizWFskX6lqbCyHqJB3LDl8gJISB5vNTHOHpvpMOMs5PYt +cRl5Mrksx5MKMqG7y5R734nMlZxQIHjL5FOoOxTBp9KeWIL/Ib89T2QDaLw1SQ+w +ihqWBJ4ZdrIMWYpP3WqM+MXWk7WAem+xsFJdR+MDgOOuobVQTy5dGBlPks/6gpjm +rO9TjfQ36ppJ3b7LdKUPeRfnYmlR5RU4oyYJ//uLbClI443RZAgxaCXX/nyc12lr +eVLUMNF2abLX4/VF63m2/Z9ACgMRfqGshPssn1NN33OonrotQoj4S3N9ZrjvzKt8 +iHcaqd60QKpfiH2A3A== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICuDCCAj2gAwIBAgIQPaVGRuu86nh/ylZVCLB0MzAKBggqhkjOPQQDAzCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIGFwLW5vcnRoZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUyNTIyMDMxNloYDzIxMjEwNTI1MjMwMzE2WjCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIGFwLW5vcnRoZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEexNURoB9KE93MEtEAlJG +obz4LS/pD2hc8Gczix1WhVvpJ8bN5zCDXaKdnDMCebetyRQsmQ2LYlfmCwpZwSDu +0zowB11Pt3I5Avu2EEcuKTlKIDMBeZ1WWuOd3Tf7MEAMo0IwQDAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBSaYbZPBvFLikSAjpa8mRJvyArMxzAOBgNVHQ8BAf8E +BAMCAYYwCgYIKoZIzj0EAwMDaQAwZgIxAOEJkuh3Zjb7Ih/zuNRd1RBqmIYcnyw0 +nwUZczKXry+9XebYj3VQxSRNadrarPWVqgIxAMg1dyGoDAYjY/L/9YElyMnvHltO +PwpJShmqHvCLc/mXMgjjYb/akK7yGthvW6j/uQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGCDCCA/CgAwIBAgIQChu3v5W1Doil3v6pgRIcVzANBgkqhkiG9w0BAQwFADCB +nDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTUwMwYDVQQDDCxB +bWF6b24gUkRTIEJldGEgdXMtZWFzdC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4G +A1UEBwwHU2VhdHRsZTAgFw0yMTA1MTgyMTM0MTVaGA8yMTIxMDUxODIyMzQxNVow +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBCZXRhIHVzLWVhc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAO +BgNVBAcMB1NlYXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC1 +FUGQ5tf3OwpDR6hGBxhUcrkwKZhaXP+1St1lSOQvjG8wXT3RkKzRGMvb7Ee0kzqI +mzKKe4ASIhtV3UUWdlNmP0EA3XKnif6N79MismTeGkDj75Yzp5A6tSvqByCgxIjK +JqpJrch3Dszoyn8+XhwDxMZtkUa5nQVdJgPzJ6ltsQ8E4SWLyLtTu0S63jJDkqYY +S7cQblk7y7fel+Vn+LS5dGTdRRhMvSzEnb6mkVBaVzRyVX90FNUED06e8q+gU8Ob +htvQlf9/kRzHwRAdls2YBhH40ZeyhpUC7vdtPwlmIyvW5CZ/QiG0yglixnL6xahL +pbmTuTSA/Oqz4UGQZv2WzHe1lD2gRHhtFX2poQZeNQX8wO9IcUhrH5XurW/G9Xwl +Sat9CMPERQn4KC3HSkat4ir2xaEUrjfg6c4XsGyh2Pk/LZ0gLKum0dyWYpWP4JmM +RQNjrInXPbMhzQObozCyFT7jYegS/3cppdyy+K1K7434wzQGLU1gYXDKFnXwkX8R +bRKgx2pHNbH5lUddjnNt75+e8m83ygSq/ZNBUz2Ur6W2s0pl6aBjwaDES4VfWYlI +jokcmrGvJNDfQWygb1k00eF2bzNeNCHwgWsuo3HSxVgc/WGsbcGrTlDKfz+g3ich +bXUeUidPhRiv5UQIVCLIHpHuin3bj9lQO/0t6p+tAQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBSFmMBgm5IsRv3hLrvDPIhcPweXYTAOBgNVHQ8B +Af8EBAMCAYYwDQYJKoZIhvcNAQEMBQADggIBAAa2EuozymOsQDJlEi7TqnyA2OhT +GXPfYqCyMJVkfrqNgcnsNpCAiNEiZbb+8sIPXnT8Ay8hrwJYEObJ5b7MHXpLuyft +z0Pu1oFLKnQxKjNxrIsCvaB4CRRdYjm1q7EqGhMGv76se9stOxkOqO9it31w/LoU +ENDk7GLsSqsV1OzYLhaH8t+MaNP6rZTSNuPrHwbV3CtBFl2TAZ7iKgKOhdFz1Hh9 +Pez0lG+oKi4mHZ7ajov6PD0W7njn5KqzCAkJR6OYmlNVPjir+c/vUtEs0j+owsMl +g7KE5g4ZpTRShyh5BjCFRK2tv0tkqafzNtxrKC5XNpEkqqVTCnLcKG+OplIEadtr +C7UWf4HyhCiR+xIyxFyR05p3uY/QQU/5uza7GlK0J+U1sBUytx7BZ+Fo8KQfPPqV +CqDCaYUksoJcnJE/KeoksyqNQys7sDGJhkd0NeUGDrFLKHSLhIwAMbEWnqGxvhli +E7sP2E5rI/I9Y9zTbLIiI8pfeZlFF8DBdoP/Hzg8pqsiE/yiXSFTKByDwKzGwNqz +F0VoFdIZcIbLdDbzlQitgGpJtvEL7HseB0WH7B2PMMD8KPJlYvPveO3/6OLzCsav ++CAkvk47NQViKMsUTKOA0JDCW+u981YRozxa3K081snhSiSe83zIPBz1ikldXxO9 +6YYLNPRrj3mi9T/f +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjSgAwIBAgIRAMkvdFnVDb0mWWFiXqnKH68wCgYIKoZIzj0EAwMwgZYx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h +em9uIFJEUyB1cy13ZXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjEwNTE5MTkxMzI0WhgPMjEyMTA1MTkyMDEzMjRaMIGWMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS +RFMgdXMtd2VzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEy86DB+9th/0A5VcWqMSWDxIUblWTt/R0 +ao6Z2l3vf2YDF2wt1A2NIOGpfQ5+WAOJO/IQmnV9LhYo+kacB8sOnXdQa6biZZkR +IyouUfikVQAKWEJnh1Cuo5YMM4E2sUt5o0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G +A1UdDgQWBBQ8u3OnecANmG8OoT7KLWDuFzZwBTAOBgNVHQ8BAf8EBAMCAYYwCgYI +KoZIzj0EAwMDaAAwZQIwQ817qkb7mWJFnieRAN+m9W3E0FLVKaV3zC5aYJUk2fcZ +TaUx3oLp3jPLGvY5+wgeAjEA6wAicAki4ZiDfxvAIuYiIe1OS/7H5RA++R8BH6qG +iRzUBM/FItFpnkus7u/eTkvo +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrzCCAjWgAwIBAgIQS/+Ryfgb/IOVEa1pWoe8oTAKBggqhkjOPQQDAzCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIGFwLXNvdXRoLTIgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjIwNjA2MjE1NDQyWhgPMjEyMjA2MDYyMjU0NDJaMIGXMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS +RFMgYXAtc291dGgtMiBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs +ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDsX6fhdUWBQpYTdseBD/P3s96Dtw2Iw +OrXKNToCnmX5nMkUGdRn9qKNiz1pw3EPzaPxShbYwQ7LYP09ENK/JN4QQjxMihxC +jLFxS85nhBQQQGRCWikDAe38mD8fSvREQKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUIh1xZiseQYFjPYKJmGbruAgRH+AwDgYDVR0PAQH/BAQDAgGGMAoG +CCqGSM49BAMDA2gAMGUCMFudS4zLy+UUGrtgNLtRMcu/DZ9BUzV4NdHxo0bkG44O +thnjl4+wTKI6VbyAbj2rkgIxAOHps8NMITU5DpyiMnKTxV8ubb/WGHrLl0BjB8Lw +ETVJk5DNuZvsIIcm7ykk6iL4Tw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGBDCCA+ygAwIBAgIQDcEmNIAVrDpUw5cH5ynutDANBgkqhkiG9w0BAQwFADCB +mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB +bWF6b24gUkRTIG1lLWNlbnRyYWwtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNV +BAcMB1NlYXR0bGUwIBcNMjIwNTA3MDA0MDIzWhgPMjEyMjA1MDcwMTQwMjNaMIGa +MQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5j +LjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMzAxBgNVBAMMKkFt +YXpvbiBSRFMgbWUtY2VudHJhbC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UE +BwwHU2VhdHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKvADk8t +Fl9bFlU5sajLPPDSOUpPAkKs6iPlz+27o1GJC88THcOvf3x0nVAcu9WYe9Qaas+4 +j4a0vv51agqyODRD/SNi2HnqW7DbtLPAm6KBHe4twl28ItB/JD5g7u1oPAHFoXMS +cH1CZEAs5RtlZGzJhcBXLFsHNv/7+SCLyZ7+2XFh9OrtgU4wMzkHoRNndhfwV5bu +17bPTwuH+VxH37zXf1mQ/KjhuJos0C9dL0FpjYBAuyZTAWhZKs8dpSe4DI544z4w +gkwUB4bC2nA1TBzsywEAHyNuZ/xRjNpWvx0ToWAA2iFJqC3VO3iKcnBplMvaUuMt +jwzVSNBnKcoabXCZL2XDLt4YTZR8FSwz05IvsmwcPB7uNTBXq3T9sjejW8QQK3vT +tzyfLq4jKmQE7PoS6cqYm+hEPm2hDaC/WP9bp3FdEJxZlPH26fq1b7BWYWhQ9pBA +Nv9zTnzdR1xohTyOJBUFQ81ybEzabqXqVXUIANqIOaNcTB09/sLJ7+zuMhp3mwBu +LtjfJv8PLuT1r63bU3seROhKA98b5KfzjvbvPSg3vws78JQyoYGbqNyDfyjVjg3U +v//AdVuPie6PNtdrW3upZY4Qti5IjP9e3kimaJ+KAtTgMRG56W0WxD3SP7+YGGbG +KhntDOkKsN39hLpn9UOafTIqFu7kIaueEy/NAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFHAems86dTwdZbLe8AaPy3kfIUVoMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQwFAAOCAgEAOBHpp0ICx81kmeoBcZTrMdJs2gnhcd85 +FoSCjXx9H5XE5rmN/lQcxxOgj8hr3uPuLdLHu+i6THAyzjrl2NA1FWiqpfeECGmy +0jm7iZsYORgGQYp/VKnDrwnKNSqlZvOuRr0kfUexwFlr34Y4VmupvEOK/RdGsd3S ++3hiemcHse9ST/sJLHx962AWMkN86UHPscJEe4+eT3f2Wyzg6La8ARwdWZSNS+WH +ZfybrncMmuiXuUdHv9XspPsqhKgtHhcYeXOGUtrwQPLe3+VJZ0LVxhlTWr9951GZ +GfmWwTV/9VsyKVaCFIXeQ6L+gjcKyEzYF8wpMtQlSc7FFqwgC4bKxvMBSaRy88Nr +lV2+tJD/fr8zGUeBK44Emon0HKDBWGX+/Hq1ZIv0Da0S+j6LbA4fusWxtGfuGha+ +luhHgVInCpALIOamiBEdGhILkoTtx7JrYppt3/Raqg9gUNCOOYlCvGhqX7DXeEfL +DGabooiY2FNWot6h04JE9nqGj5QqT8D6t/TL1nzxhRPzbcSDIHUd/b5R+a0bAA+7 +YTU6JqzEVCWKEIEynYmqikgLMGB/OzWsgyEL6822QW6hJAQ78XpbNeCzrICF4+GC +7KShLnwuWoWpAb26268lvOEvCTFM47VC6jNQl97md+2SA9Ma81C9wflid2M83Wle +cuLMVcQZceE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIQAhAteLRCvizAElaWORFU2zANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB +bWF6b24gUkRTIG1lLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUyMDE3MDkxNloYDzIwNjEwNTIwMTgwOTE2WjCBmDEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6 +b24gUkRTIG1lLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT +ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+qg7JAcOVKjh +N83SACnBFZPyB63EusfDr/0V9ZdL8lKcmZX9sv/CqoBo3N0EvBqHQqUUX6JvFb7F +XrMUZ740kr28gSRALfXTFgNODjXeDsCtEkKRTkac/UM8xXHn+hR7UFRPHS3e0GzI +iLiwQWDkr0Op74W8aM0CfaVKvh2bp4BI1jJbdDnQ9OKXpOxNHGUf0ZGb7TkNPkgI +b2CBAc8J5o3H9lfw4uiyvl6Fz5JoP+A+zPELAioYBXDrbE7wJeqQDJrETWqR9VEK +BXURCkVnHeaJy123MpAX2ozf4pqk0V0LOEOZRS29I+USF5DcWr7QIXR/w2I8ws1Q +7ys+qbE+kQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQFJ16n +1EcCMOIhoZs/F9sR+Jy++zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggEBAOc5nXbT3XTDEZsxX2iD15YrQvmL5m13B3ImZWpx/pqmObsgx3/dg75rF2nQ +qS+Vl+f/HLh516pj2BPP/yWCq12TRYigGav8UH0qdT3CAClYy2o+zAzUJHm84oiB +ud+6pFVGkbqpsY+QMpJUbZWu52KViBpJMYsUEy+9cnPSFRVuRAHjYynSiLk2ZEjb +Wkdc4x0nOZR5tP0FgrX0Ve2KcjFwVQJVZLgOUqmFYQ/G0TIIGTNh9tcmR7yp+xJR +A2tbPV2Z6m9Yxx4E8lLEPNuoeouJ/GR4CkMEmF8cLwM310t174o3lKKUXJ4Vs2HO +Wj2uN6R9oI+jGLMSswTzCNV1vgc= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICuDCCAj6gAwIBAgIRAOocLeZWjYkG/EbHmscuy8gwCgYIKoZIzj0EAwMwgZsx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE0MDIGA1UEAwwrQW1h +em9uIFJEUyBhcC1zb3V0aGVhc3QtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UE +BwwHU2VhdHRsZTAgFw0yMTA1MjEyMTUwMDFaGA8yMTIxMDUyMTIyNTAwMVowgZsx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE0MDIGA1UEAwwrQW1h +em9uIFJEUyBhcC1zb3V0aGVhc3QtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UE +BwwHU2VhdHRsZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABCEr3jq1KtRncnZfK5cq +btY0nW6ZG3FMbh7XwBIR6Ca0f8llGZ4vJEC1pXgiM/4Dh045B9ZIzNrR54rYOIfa +2NcYZ7mk06DjIQML64hbAxbQzOAuNzLPx268MrlL2uW2XaNCMEAwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUln75pChychwN4RfHl+tOinMrfVowDgYDVR0PAQH/ +BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMGiyPINRU1mwZ4Crw01vpuPvxZxb2IOr +yX3RNlOIu4We1H+5dQk5tIvH8KGYFbWEpAIxAO9NZ6/j9osMhLgZ0yj0WVjb+uZx +YlZR9fyFisY/jNfX7QhSk+nrc3SFLRUNtpXrng== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEBTCCAu2gAwIBAgIRAKiaRZatN8eiz9p0s0lu0rQwDQYJKoZIhvcNAQELBQAw +gZoxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEzMDEGA1UEAwwq +QW1hem9uIFJEUyBjYS1jZW50cmFsLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYD +VQQHDAdTZWF0dGxlMCAXDTIxMDUyMTIyMDIzNVoYDzIwNjEwNTIxMjMwMjM1WjCB +mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB +bWF6b24gUkRTIGNhLWNlbnRyYWwtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNV +BAcMB1NlYXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCygVMf +qB865IR9qYRBRFHn4eAqGJOCFx+UbraQZmjr/mnRqSkY+nhbM7Pn/DWOrRnxoh+w +q5F9ZxdZ5D5T1v6kljVwxyfFgHItyyyIL0YS7e2h7cRRscCM+75kMedAP7icb4YN +LfWBqfKHbHIOqvvQK8T6+Emu/QlG2B5LvuErrop9K0KinhITekpVIO4HCN61cuOe +CADBKF/5uUJHwS9pWw3uUbpGUwsLBuhJzCY/OpJlDqC8Y9aToi2Ivl5u3/Q/sKjr +6AZb9lx4q3J2z7tJDrm5MHYwV74elGSXoeoG8nODUqjgklIWAPrt6lQ3WJpO2kug +8RhCdSbWkcXHfX95AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FOIxhqTPkKVqKBZvMWtKewKWDvDBMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0B +AQsFAAOCAQEAqoItII89lOl4TKvg0I1EinxafZLXIheLcdGCxpjRxlZ9QMQUN3yb +y/8uFKBL0otbQgJEoGhxm4h0tp54g28M6TN1U0332dwkjYxUNwvzrMaV5Na55I2Z +1hq4GB3NMXW+PvdtsgVOZbEN+zOyOZ5MvJHEQVkT3YRnf6avsdntltcRzHJ16pJc +Y8rR7yWwPXh1lPaPkxddrCtwayyGxNbNmRybjR48uHRhwu7v2WuAMdChL8H8bp89 +TQLMrMHgSbZfee9hKhO4Zebelf1/cslRSrhkG0ESq6G5MUINj6lMg2g6F0F7Xz2v +ncD/vuRN5P+vT8th/oZ0Q2Gc68Pun0cn/g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID/zCCAuegAwIBAgIRAJYlnmkGRj4ju/2jBQsnXJYwDQYJKoZIhvcNAQELBQAw +gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn +QW1hem9uIFJEUyB1cy1lYXN0LTIgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUyMTIzMDQ0NFoYDzIwNjEwNTIyMDAwNDQ0WjCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIHVzLWVhc3QtMiBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl +YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC74V3eigv+pCj5 +nqDBqplY0Jp16pTeNB06IKbzb4MOTvNde6QjsZxrE1xUmprT8LxQqN9tI3aDYEYk +b9v4F99WtQVgCv3Y34tYKX9NwWQgwS1vQwnIR8zOFBYqsAsHEkeJuSqAB12AYUSd +Zv2RVFjiFmYJho2X30IrSLQfS/IE3KV7fCyMMm154+/K1Z2IJlcissydEAwgsUHw +edrE6CxJVkkJ3EvIgG4ugK/suxd8eEMztaQYJwSdN8TdfT59LFuSPl7zmF3fIBdJ +//WexcQmGabaJ7Xnx+6o2HTfkP8Zzzzaq8fvjAcvA7gyFH5EP26G2ZqMG+0y4pTx +SPVTrQEXAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIWWuNEF +sUMOC82XlfJeqazzrkPDMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC +AQEAgClmxcJaQTGpEZmjElL8G2Zc8lGc+ylGjiNlSIw8X25/bcLRptbDA90nuP+q +zXAMhEf0ccbdpwxG/P5a8JipmHgqQLHfpkvaXx+0CuP++3k+chAJ3Gk5XtY587jX ++MJfrPgjFt7vmMaKmynndf+NaIJAYczjhJj6xjPWmGrjM3MlTa9XesmelMwP3jep +bApIWAvCYVjGndbK9byyMq1nyj0TUzB8oJZQooaR3MMjHTmADuVBylWzkRMxbKPl +4Nlsk4Ef1JvIWBCzsMt+X17nuKfEatRfp3c9tbpGlAE/DSP0W2/Lnayxr4RpE9ds +ICF35uSis/7ZlsftODUe8wtpkQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjOgAwIBAgIQS7vMpOTVq2Jw457NdZ2ffjAKBggqhkjOPQQDAzCBljEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMS8wLQYDVQQDDCZBbWF6 +b24gUkRTIGNhLXdlc3QtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2Vh +dHRsZTAgFw0yMzA5MTkyMjExNDNaGA8yMTIzMDkxOTIzMTE0M1owgZYxCzAJBgNV +BAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYD +VQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1hem9uIFJE +UyBjYS13ZXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1NlYXR0bGUw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAARdgGSs/F2lpWKqS1ZpcmatFED1JurmNbXG +Sqhv1A/geHrKCS15MPwjtnfZiujYKY4fNkCCUseoGDwkC4281nwkokvnfWR1/cXy +LxfACoXNxsI4b+37CezSUBl48/5p1/OjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFFhLokGBuJGwKJhZcYSYKyZIitJtMA4GA1UdDwEB/wQEAwIBhjAKBggq +hkjOPQQDAwNpADBmAjEA8aQQlzJRHbqFsRY4O3u/cN0T8dzjcqnYn4NV1w+jvhzt +QPJLB+ggGyQhoFR6G2UrAjEA0be8OP5MWXD8d01KKbo5Dpy6TwukF5qoJmkFJKS3 +bKfEMvFWxXoV06HNZFWdI80u +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +Pqj2 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +KDPtFEk= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICtzCCAj2gAwIBAgIQVZ5Y/KqjR4XLou8MCD5pOjAKBggqhkjOPQQDAzCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIGFwLXNvdXRoZWFzdC00IFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIyMDUyNTE2NTgzM1oYDzIxMjIwNTI1MTc1ODMzWjCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIGFwLXNvdXRoZWFzdC00IFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEbo473OmpD5vkckdJajXg +brhmNFyoSa0WCY1njuZC2zMFp3zP6rX4I1r3imrYnJd9pFH/aSiV/r6L5ACE5RPx +4qdg5SQ7JJUaZc3DWsTOiOed7BCZSzM+KTYK/2QzDMApo0IwQDAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTmogc06+1knsej1ltKUOdWFvwgsjAOBgNVHQ8BAf8E +BAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIxAIs7TlLMbGTWNXpGiKf9DxaM07d/iDHe +F/Vv/wyWSTGdobxBL6iArQNVXz0Gr4dvPAIwd0rsoa6R0x5mtvhdRPtM37FYrbHJ +pbV+OMusQqcSLseunLBoCHenvJW0QOCQ8EDY +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGBTCCA+2gAwIBAgIRAO9dVdiLTEGO8kjUFExJmgowDQYJKoZIhvcNAQEMBQAw +gZoxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEzMDEGA1UEAwwq +QW1hem9uIFJEUyBpbC1jZW50cmFsLTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAwDgYD +VQQHDAdTZWF0dGxlMCAXDTIyMTIwMjIwMjYwOFoYDzIxMjIxMjAyMjEyNjA4WjCB +mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB +bWF6b24gUkRTIGlsLWNlbnRyYWwtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNV +BAcMB1NlYXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDkVHmJ +bUc8CNDGBcgPmXHSHj5dS1PDnnpk3doCu6pahyYXW8tqAOmOqsDuNz48exY7YVy4 +u9I9OPBeTYB9ZUKwxq+1ZNLsr1cwVz5DdOyDREVFOjlU4rvw0eTgzhP5yw/d+Ai/ ++WmPebZG0irwPKN2f60W/KJ45UNtR+30MT8ugfnPuSHWjjV+dqCOCp/mj8nOCckn +k8GoREwjuTFJMKInpQUC0BaVVX6LiIdgtoLY4wdx00EqNBuROoRTAvrked0jvm7J +UI39CSYxhNZJ9F6LdESZXjI4u2apfNQeSoy6WptxFHr+kh2yss1B2KT6lbwGjwWm +l9HODk9kbBNSy2NeewAms36q+p8wSLPavL28IRfK0UaBAiN1hr2a/2RDGCwOJmw6 +5erRC5IIX5kCStyXPEGhVPp18EvMuBd37eLIxjZBBO8AIDf4Ue8QmxSeZH0cT204 +3/Bd6XR6+Up9iMTxkHr1URcL1AR8Zd62lg/lbEfxePNMK9mQGxKP8eTMG5AjtW9G +TatEoRclgE0wZQalXHmKpBNshyYdGqQZhzL1MxCxWzfHNgZkTKIsdzxrjnP7RiBR +jdRH0YhXn6Y906QfLwMCaufwfQ5J8+nj/tu7nG138kSxsu6VUkhnQJhUcUsxuHD/ +NnBx0KGVEldtZiZf7ccgtRVp1lA0OrVtq3ZLMQIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBQ2WC3p8rWeE2N0S4Om01KsNLpk/jAOBgNVHQ8BAf8E +BAMCAYYwDQYJKoZIhvcNAQEMBQADggIBAFFEVDt45Obr6Ax9E4RMgsKjj4QjMFB9 +wHev1jL7hezl/ULrHuWxjIusaIZEIcKfn+v2aWtqOq13P3ht7jV5KsV29CmFuCdQ +q3PWiAXVs+hnMskTOmGMDnptqd6/UuSIha8mlOKKAvnmRQJvfX9hIfb/b/mVyKWD +uvTTmcy3cOTJY5ZIWGyzuvmcqA0YNcb7rkJt/iaLq4RX3/ofq4y4w36hefbcvj++ +pXHOmXk3dAej3y6SMBOUcGMyCJcCluRPNYKDTLn+fitcPxPC3JG7fI5bxQ0D6Hpa +qbyGBQu96sfahQyMc+//H8EYlo4b0vPeS5RFFXJS/VBf0AyNT4vVc7H17Q6KjeNp +wEARqsIa7UalHx9MnxrQ/LSTTxiC8qmDkIFuQtw8iQMN0SoL5S0eCZNRD31awgaY +y1PvY8JMN549ugIUjOXnown/OxharLW1evWUraU5rArq3JfeFpPXl4K/u10T5SCL +iJRoxFilGPMFE3hvnmbi5rEy8wRUn7TpLb4I4s/CB/lT2qZTPqvQHwxKCnMm9BKF +NHb4rLL5dCvUi5NJ6fQ/exOoGdOVSfT7jqFeq2TtNunERSz9vpriweliB6iIe1Al +Thj8aEs1GqA764rLVGA+vUe18NhjJm9EemrdIzjSQFy/NdbN/DMaHqEzJogWloAI +izQWYnCS19TJ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICvTCCAkOgAwIBAgIQCIY7E/bFvFN2lK9Kckb0dTAKBggqhkjOPQQDAzCBnjEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTcwNQYDVQQDDC5BbWF6 +b24gUkRTIFByZXZpZXcgdXMtZWFzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYD +VQQHDAdTZWF0dGxlMCAXDTIxMDUxODIxMDUxMFoYDzIxMjEwNTE4MjIwNTEwWjCB +njELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTcwNQYDVQQDDC5B +bWF6b24gUkRTIFByZXZpZXcgdXMtZWFzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEMI0hzf1JCEOI +Eue4+DmcNnSs2i2UaJxHMrNGGfU7b42a7vwP53F7045ffHPBGP4jb9q02/bStZzd +VHqfcgqkSRI7beBKjD2mfz82hF/wJSITTgCLs+NRpS6zKMFOFHUNo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBS8uF/6hk5mPLH4qaWv9NVZaMmyTjAOBgNV +HQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIxAO7Pu9wzLyM0X7Q08uLIL+vL +qaxe3UFuzFTWjM16MLJHbzLf1i9IDFKz+Q4hXCSiJwIwClMBsqT49BPUxVsJnjGr +EbyEk6aOOVfY1p2yQL649zh3M4h8okLnwf+bYIb1YpeU +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIQY+JhwFEQTe36qyRlUlF8ozANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB +bWF6b24gUkRTIGFmLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUxOTE5MjQxNloYDzIwNjEwNTE5MjAyNDE2WjCBmDEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6 +b24gUkRTIGFmLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT +ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnIye77j6ev40 +8wRPyN2OdKFSUfI9jB20Or2RLO+RDoL43+USXdrze0Wv4HMRLqaen9BcmCfaKMp0 +E4SFo47bXK/O17r6G8eyq1sqnHE+v288mWtYH9lAlSamNFRF6YwA7zncmE/iKL8J +0vePHMHP/B6svw8LULZCk+nZk3tgxQn2+r0B4FOz+RmpkoVddfqqUPMbKUxhM2wf +fO7F6bJaUXDNMBPhCn/3ayKCjYr49ErmnpYV2ZVs1i34S+LFq39J7kyv6zAgbHv9 ++/MtRMoRB1CjpqW0jIOZkHBdYcd1o9p1zFn591Do1wPkmMsWdjIYj+6e7UXcHvOB +2+ScIRAcnwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQGtq2W +YSyMMxpdQ3IZvcGE+nyZqTAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggEBAEgoP3ixJsKSD5FN8dQ01RNHERl/IFbA7TRXfwC+L1yFocKnQh4Mp/msPRSV ++OeHIvemPW/wtZDJzLTOFJ6eTolGekHK1GRTQ6ZqsWiU2fmiOP8ks4oSpI+tQ9Lw +VrfZqTiEcS5wEIqyfUAZZfKDo7W1xp+dQWzfczSBuZJZwI5iaha7+ILM0r8Ckden +TVTapc5pLSoO15v0ziRuQ2bT3V3nwu/U0MRK44z+VWOJdSiKxdnOYDs8hFNnKhfe +klbTZF7kW7WbiNYB43OaAQBJ6BALZsIskEaqfeZT8FD71uN928TcEQyBDXdZpRN+ +iGQZDGhht0r0URGMDSs9waJtTfA= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF/jCCA+agAwIBAgIQXY/dmS+72lZPranO2JM9jjANBgkqhkiG9w0BAQwFADCB +lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB +bWF6b24gUkRTIGFwLWVhc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcM +B1NlYXR0bGUwIBcNMjEwNTI1MjEzNDUxWhgPMjEyMTA1MjUyMjM0NTFaMIGXMQsw +CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET +MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv +biBSRFMgYXAtZWFzdC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwHU2Vh +dHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMyW9kBJjD/hx8e8 +b5E1sF42bp8TXsz1htSYE3Tl3T1Aq379DfEhB+xa/ASDZxt7/vwa81BkNo4M6HYq +okYIXeE7cu5SnSgjWXqcERhgPevtAwgmhdE3yREe8oz2DyOi2qKKZqah+1gpPaIQ +fK0uAqoeQlyHosye3KZZKkDHBatjBsQ5kf8lhuf7wVulEZVRHY2bP2X7N98PfbpL +QdH7mWXzDtJJ0LiwFwds47BrkgK1pkHx2p1mTo+HMkfX0P6Fq1atkVC2RHHtbB/X +iYyH7paaHBzviFrhr679zNqwXIOKlbf74w3mS11P76rFn9rS1BAH2Qm6eY5S/Fxe +HEKXm4kjPN63Zy0p3yE5EjPt54yPkvumOnT+RqDGJ2HCI9k8Ehcbve0ogfdRKNqQ +VHWYTy8V33ndQRHZlx/CuU1yN61TH4WSoMly1+q1ihTX9sApmlQ14B2pJi/9DnKW +cwECrPy1jAowC2UJ45RtC8UC05CbP9yrIy/7Noj8gQDiDOepm+6w1g6aNlWoiuQS +kyI6nzz1983GcnOHya73ga7otXo0Qfg9jPghlYiMomrgshlSLDHZG0Ib/3hb8cnR +1OcN9FpzNmVK2Ll1SmTMLrIhuCkyNYX9O/bOknbcf706XeESxGduSkHEjIw/k1+2 +Atteoq5dT6cwjnJ9hyhiueVlVkiDAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8w +HQYDVR0OBBYEFLUI+DD7RJs+0nRnjcwIVWzzYSsFMA4GA1UdDwEB/wQEAwIBhjAN +BgkqhkiG9w0BAQwFAAOCAgEAb1mcCHv4qMQetLGTBH9IxsB2YUUhr5dda0D2BcHr +UtDbfd0VQs4tux6h/6iKwHPx0Ew8fuuYj99WknG0ffgJfNc5/fMspxR/pc1jpdyU +5zMQ+B9wi0lOZPO9uH7/pr+d2odcNEy8zAwqdv/ihsTwLmGP54is9fVbsgzNW1cm +HKAVL2t/Ope+3QnRiRilKCN1lzhav4HHdLlN401TcWRWKbEuxF/FgxSO2Hmx86pj +e726lweCTMmnq/cTsPOVY0WMjs0or3eHDVlyLgVeV5ldyN+ptg3Oit60T05SRa58 +AJPTaVKIcGQ/gKkKZConpu7GDofT67P/ox0YNY57LRbhsx9r5UY4ROgz7WMQ1yoS +Y+19xizm+mBm2PyjMUbfwZUyCxsdKMwVdOq5/UmTmdms+TR8+m1uBHPOTQ2vKR0s +Pd/THSzPuu+d3dbzRyDSLQbHFFneG760CUlD/ZmzFlQjJ89/HmAmz8IyENq+Sjhx +Jgzy+FjVZb8aRUoYLlnffpUpej1n87Ynlr1GrvC4GsRpNpOHlwuf6WD4W0qUTsC/ +C9JO+fBzUj/aWlJzNcLEW6pte1SB+EdkR2sZvWH+F88TxemeDrV0jKJw5R89CDf8 +ZQNfkxJYjhns+YeV0moYjqQdc7tq4i04uggEQEtVzEhRLU5PE83nlh/K2NZZm8Kj +dIA= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID/zCCAuegAwIBAgIRAPVSMfFitmM5PhmbaOFoGfUwDQYJKoZIhvcNAQELBQAw +gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn +QW1hem9uIFJEUyB1cy1lYXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUyNTIyMzQ1N1oYDzIwNjEwNTI1MjMzNDU3WjCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIHVzLWVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl +YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDu9H7TBeGoDzMr +dxN6H8COntJX4IR6dbyhnj5qMD4xl/IWvp50lt0VpmMd+z2PNZzx8RazeGC5IniV +5nrLg0AKWRQ2A/lGGXbUrGXCSe09brMQCxWBSIYe1WZZ1iU1IJ/6Bp4D2YEHpXrW +bPkOq5x3YPcsoitgm1Xh8ygz6vb7PsvJvPbvRMnkDg5IqEThapPjmKb8ZJWyEFEE +QRrkCIRueB1EqQtJw0fvP4PKDlCJAKBEs/y049FoOqYpT3pRy0WKqPhWve+hScMd +6obq8kxTFy1IHACjHc51nrGII5Bt76/MpTWhnJIJrCnq1/Uc3Qs8IVeb+sLaFC8K +DI69Sw6bAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE7PCopt +lyOgtXX0Y1lObBUxuKaCMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC +AQEAFj+bX8gLmMNefr5jRJfHjrL3iuZCjf7YEZgn89pS4z8408mjj9z6Q5D1H7yS +jNETVV8QaJip1qyhh5gRzRaArgGAYvi2/r0zPsy+Tgf7v1KGL5Lh8NT8iCEGGXwF +g3Ir+Nl3e+9XUp0eyyzBIjHtjLBm6yy8rGk9p6OtFDQnKF5OxwbAgip42CD75r/q +p421maEDDvvRFR4D+99JZxgAYDBGqRRceUoe16qDzbMvlz0A9paCZFclxeftAxv6 +QlR5rItMz/XdzpBJUpYhdzM0gCzAzdQuVO5tjJxmXhkSMcDP+8Q+Uv6FA9k2VpUV +E/O5jgpqUJJ2Hc/5rs9VkAPXeA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrzCCAjWgAwIBAgIQW0yuFCle3uj4vWiGU0SaGzAKBggqhkjOPQQDAzCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIGFmLXNvdXRoLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjEwNTE5MTkzNTE2WhgPMjEyMTA1MTkyMDM1MTZaMIGXMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS +RFMgYWYtc291dGgtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs +ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDPiKNZSaXs3Un/J/v+LTsFDANHpi7en +oL2qh0u0DoqNzEBTbBjvO23bLN3k599zh6CY3HKW0r2k1yaIdbWqt4upMCRCcUFi +I4iedAmubgzh56wJdoMZztjXZRwDthTkJKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUWbYkcrvVSnAWPR5PJhIzppcAnZIwDgYDVR0PAQH/BAQDAgGGMAoG +CCqGSM49BAMDA2gAMGUCMCESGqpat93CjrSEjE7z+Hbvz0psZTHwqaxuiH64GKUm +mYynIiwpKHyBrzjKBmeDoQIxANGrjIo6/b8Jl6sdIZQI18V0pAyLfLiZjlHVOnhM +MOTVgr82ZuPoEHTX78MxeMnYlw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIRAIbsx8XOl0sgTNiCN4O+18QwDQYJKoZIhvcNAQELBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1ub3J0aGVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTI1MjE1NDU4WhgPMjA2MTA1MjUyMjU0NTha +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgYXAtbm9ydGhlYXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +tROxwXWCgn5R9gI/2Ivjzaxc0g95ysBjoJsnhPdJEHQb7w3y2kWrVWU3Y9fOitgb +CEsnEC3PrhRnzNVW0fPsK6kbvOeCmjvY30rdbxbc8h+bjXfGmIOgAkmoULEr6Hc7 +G1Q/+tvv4lEwIs7bEaf+abSZxRJbZ0MBxhbHn7UHHDiMZYvzK+SV1MGCxx7JVhrm +xWu3GC1zZCsGDhB9YqY9eR6PmjbqA5wy8vqbC57dZZa1QVtWIQn3JaRXn+faIzHx +nLMN5CEWihsdmHBXhnRboXprE/OS4MFv1UrQF/XM/h5RBeCywpHePpC+Oe1T3LNC +iP8KzRFrjC1MX/WXJnmOVQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBS33XbXAUMs1znyZo4B0+B3D68WFTAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI +hvcNAQELBQADggEBADuadd2EmlpueY2VlrIIPC30QkoA1EOSoCmZgN6124apkoY1 +HiV4r+QNPljN4WP8gmcARnNkS7ZeR4fvWi8xPh5AxQCpiaBMw4gcbTMCuKDV68Pw +P2dZCTMspvR3CDfM35oXCufdtFnxyU6PAyINUqF/wyTHguO3owRFPz64+sk3r2pT +WHmJjG9E7V+KOh0s6REgD17Gqn6C5ijLchSrPUHB0wOIkeLJZndHxN/76h7+zhMt +fFeNxPWHY2MfpcaLjz4UREzZPSB2U9k+y3pW1omCIcl6MQU9itGx/LpQE+H3ZeX2 +M2bdYd5L+ow+bdbGtsVKOuN+R9Dm17YpswF+vyQ= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +Em4Hy3A= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEBDCCAuygAwIBAgIQFn6AJ+uxaPDpNVx7174CpjANBgkqhkiG9w0BAQsFADCB +mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB +bWF6b24gUkRTIGlsLWNlbnRyYWwtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNV +BAcMB1NlYXR0bGUwIBcNMjIxMjAyMjAxNDA4WhgPMjA2MjEyMDIyMTE0MDhaMIGa +MQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5j +LjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMzAxBgNVBAMMKkFt +YXpvbiBSRFMgaWwtY2VudHJhbC0xIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UE +BwwHU2VhdHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL2xGTSJ +fXorki/dkkTqdLyv4U1neeFYEyUCPN/HJ7ZloNwhj8RBrHYhZ4qtvUAvN+rs8fUm +L0wmaL69ye61S+CSfDzNwBDGwOzUm/cc1NEJOHCm8XA0unBNBvpJTjsFk2LQ+rz8 +oU0lVV4mjnfGektrTDeADonO1adJvUTYmF6v1wMnykSkp8AnW9EG/6nwcAJuAJ7d +BfaLThm6lfxPdsBNG81DLKi2me2TLQ4yl+vgRKJi2fJWwA77NaDqQuD5upRIcQwt +5noJt2kFFmeiro98ZMMRaDTHAHhJfWkwkw5f2QNIww7T4r85IwbQCgJVRo4m4ZTC +W/1eiEccU2407mECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +DNhVvGHzKXv0Yh6asK0apP9jJlUwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQCoEVTUY/rF9Zrlpb1Y1hptEguw0i2pCLakcmv3YNj6thsubbGeGx8Z +RjUA/gPKirpoae2HU1y64WEu7akwr6pdTRtXXjbe9NReT6OW/0xAwceSXCOiStqS +cMsWWTGg6BA3uHqad5clqITjDZr1baQ8X8en4SXRBxXyhJXbOkB60HOQeFR9CNeh +pJdrWLeNYXwU0Z59juqdVMGwvDAYdugWUhW2rhafVUXszfRA5c8Izc+E31kq90aY +LmxFXUHUfG0eQOmxmg+Z/nG7yLUdHIFA3id8MRh22hye3KvRdQ7ZVGFni0hG2vQQ +Q01AvD/rhzyjg0czzJKLK9U/RttwdMaV +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +jGKc3k6UNxpg +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +0Ws= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICtDCCAjqgAwIBAgIRAMyaTlVLN0ndGp4ffwKAfoMwCgYIKoZIzj0EAwMwgZkx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEyMDAGA1UEAwwpQW1h +em9uIFJEUyBtZS1jZW50cmFsLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcM +B1NlYXR0bGUwIBcNMjIwNTA3MDA0NDM3WhgPMjEyMjA1MDcwMTQ0MzdaMIGZMQsw +CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET +MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMjAwBgNVBAMMKUFtYXpv +biBSRFMgbWUtY2VudHJhbC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdT +ZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE19nCV1nsI6CohSor13+B25cr +zg+IHdi9Y3L7ziQnHWI6yjBazvnKD+oC71aRRlR8b5YXsYGUQxWzPLHN7EGPcSGv +bzA9SLG1KQYCJaQ0m9Eg/iGrwKWOgylbhVw0bCxoo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS4KsknsJXM9+QPEkBdZxUPaLr11zAOBgNVHQ8BAf8EBAMC +AYYwCgYIKoZIzj0EAwMDaAAwZQIxAJaRgrYIEfXQMZQQDxMTYS0azpyWSseQooXo +L3nYq4OHGBgYyQ9gVjvRYWU85PXbfgIwdi82DtANQFkCu+j+BU0JBY/uRKPEeYzo +JG92igKIcXPqCoxIJ7lJbbzmuf73gQu5 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +Tspdjbg= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIRAJZFh4s9aZGzKaTMLrSb4acwDQYJKoZIhvcNAQELBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBCZXRhIHVzLWVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTE4MjEyODQxWhgPMjA2MTA1MTgyMjI4NDFa +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgQmV0YSB1cy1lYXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +17i2yoU6diep+WrqxIn2CrDEO2NdJVwWTSckx4WMZlLpkQDoymSmkNHjq9ADIApD +A31Cx+843apL7wub8QkFZD0Tk7/ThdHWJOzcAM3ov98QBPQfOC1W5zYIIRP2F+vQ +TRETHQnLcW3rLv0NMk5oQvIKpJoC9ett6aeVrzu+4cU4DZVWYlJUoC/ljWzCluau +8blfW0Vwin6OB7s0HCG5/wijQWJBU5SrP/KAIPeQi1GqG5efbqAXDr/ple0Ipwyo +Xjjl73LenGUgqpANlC9EAT4i7FkJcllLPeK3NcOHjuUG0AccLv1lGsHAxZLgjk/x +z9ZcnVV9UFWZiyJTKxeKPwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBRWyMuZUo4gxCR3Luf9/bd2AqZ7CjAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI +hvcNAQELBQADggEBAIqN2DlIKlvDFPO0QUZQVFbsi/tLdYM98/vvzBpttlTGVMyD +gJuQeHVz+MnhGIwoCGOlGU3OOUoIlLAut0+WG74qYczn43oA2gbMd7HoD7oL/IGg +njorBwJVcuuLv2G//SqM3nxGcLRtkRnQ+lvqPxMz9+0fKFUn6QcIDuF0QSfthLs2 +WSiGEPKO9c9RSXdRQ4pXA7c3hXng8P4A2ZmdciPne5Nu4I4qLDGZYRrRLRkNTrOi +TyS6r2HNGUfgF7eOSeKt3NWL+mNChcYj71/Vycf5edeczpUgfnWy9WbPrK1svKyl +aAs2xg+X6O8qB+Mnj2dNBzm+lZIS3sIlm+nO9sg= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjSgAwIBAgIRAPAlEk8VJPmEzVRRaWvTh2AwCgYIKoZIzj0EAwMwgZYx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h +em9uIFJEUyB1cy1lYXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjEwNTI1MjI0MTU1WhgPMjEyMTA1MjUyMzQxNTVaMIGWMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS +RFMgdXMtZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEx5xjrup8II4HOJw15NTnS3H5yMrQGlbj +EDA5MMGnE9DmHp5dACIxmPXPMe/99nO7wNdl7G71OYPCgEvWm0FhdvVUeTb3LVnV +BnaXt32Ek7/oxGk1T+Df03C+W0vmuJ+wo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G +A1UdDgQWBBTGXmqBWN/1tkSea4pNw0oHrjk2UDAOBgNVHQ8BAf8EBAMCAYYwCgYI +KoZIzj0EAwMDaAAwZQIxAIqqZWCSrIkZ7zsv/FygtAusW6yvlL935YAWYPVXU30m +jkMFLM+/RJ9GMvnO8jHfCgIwB+whlkcItzE9CRQ6CsMo/d5cEHDUu/QW6jSIh9BR +OGh9pTYPVkUbBiKPA7lVVhre +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +nWS0 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICtDCCAjmgAwIBAgIQKKqVZvk6NsLET+uYv5myCzAKBggqhkjOPQQDAzCBmTEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTIwMAYDVQQDDClBbWF6 +b24gUkRTIGlsLWNlbnRyYWwtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwH +U2VhdHRsZTAgFw0yMjEyMDIyMDMyMjBaGA8yMTIyMTIwMjIxMzIyMFowgZkxCzAJ +BgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMw +EQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEyMDAGA1UEAwwpQW1hem9u +IFJEUyBpbC1jZW50cmFsLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASYwfvj8BmvLAP6UkNQ4X4dXBB/ +webBO7swW+8HnFN2DAu+Cn/lpcDpu+dys1JmkVX435lrCH3oZjol0kCDIM1lF4Cv ++78yoY1Jr/YMat22E4iz4AZd9q0NToS7+ZA0r2yjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFO/8Py16qPr7J2GWpvxlTMB+op7XMA4GA1UdDwEB/wQEAwIB +hjAKBggqhkjOPQQDAwNpADBmAjEAwk+rg788+u8JL6sdix7l57WTo8E/M+o3TO5x +uRuPdShrBFm4ArGR2PPs4zCQuKgqAjEAi0TA3PVqAxKpoz+Ps8/054p9WTgDfBFZ +i/lm2yTaPs0xjY6FNWoy7fsVw5oEKxOn +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGCTCCA/GgAwIBAgIRAOY7gfcBZgR2tqfBzMbFQCUwDQYJKoZIhvcNAQEMBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtNCBSb290IENBIFJTQTQwOTYgRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjIwNTI1MTY1NDU5WhgPMjEyMjA1MjUxNzU0NTla +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgYXAtc291dGhlYXN0LTQgUm9vdCBDQSBSU0E0MDk2IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +lfxER43FuLRdL08bddF0YhbCP+XXKj1A/TFMXmd2My8XDei8rPXFYyyjMig9+xZw +uAsIxLwz8uiA26CKA8bCZKg5VG2kTeOJAfvBJaLv1CZefs3Z4Uf1Sjvm6MF2yqEj +GoORfyfL9HiZFTDuF/hcjWoKYCfMuG6M/wO8IbdICrX3n+BiYQJu/pFO660Mg3h/ +8YBBWYDbHoCiH/vkqqJugQ5BM3OI5nsElW51P1icEEqti4AZ7JmtSv9t7fIFBVyR +oaEyOgpp0sm193F/cDJQdssvjoOnaubsSYm1ep3awZAUyGN/X8MBrPY95d0hLhfH +Ehc5Icyg+hsosBljlAyksmt4hFQ9iBnWIz/ZTfGMck+6p3HVL9RDgvluez+rWv59 +8q7omUGsiPApy5PDdwI/Wt/KtC34/2sjslIJfvgifdAtkRPkhff1WEwER00ADrN9 +eGGInaCpJfb1Rq8cV2n00jxg7DcEd65VR3dmIRb0bL+jWK62ni/WdEyomAOMfmGj +aWf78S/4rasHllWJ+QwnaUYY3u6N8Cgio0/ep4i34FxMXqMV3V0/qXdfhyabi/LM +wCxNo1Dwt+s6OtPJbwO92JL+829QAxydfmaMTeHBsgMPkG7RwAekeuatKGHNsc2Z +x2Q4C2wVvOGAhcHwxfM8JfZs3nDSZJndtVVnFlUY0UECAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUpnG7mWazy6k97/tb5iduRB3RXgQwDgYDVR0P +AQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQCDLqq1Wwa9Tkuv7vxBnIeVvvFF +ecTn+P+wJxl9Qa2ortzqTHZsBDyJO62d04AgBwiDXkJ9a+bthgG0H1J7Xee8xqv1 +xyX2yKj24ygHjspLotKP4eDMdDi5TYq+gdkbPmm9Q69B1+W6e049JVGXvWG8/7kU +igxeuCYwtCCdUPRLf6D8y+1XMGgVv3/DSOHWvTg3MJ1wJ3n3+eve3rjGdRYWZeJu +k21HLSZYzVrCtUsh2YAeLnUbSxVuT2Xr4JehYe9zW5HEQ8Je/OUfnCy9vzoN/ITw +osAH+EBJQey7RxEDqMwCaRefH0yeHFcnOll0OXg/urnQmwbEYzQ1uutJaBPsjU0J +Qf06sMxI7GiB5nPE+CnI2sM6A9AW9kvwexGXpNJiLxF8dvPQthpOKGcYu6BFvRmt +6ctfXd9b7JJoVqMWuf5cCY6ihpk1e9JTlAqu4Eb/7JNyGiGCR40iSLvV28un9wiE +plrdYxwcNYq851BEu3r3AyYWw/UW1AKJ5tM+/Gtok+AphMC9ywT66o/Kfu44mOWm +L3nSLSWEcgfUVgrikpnyGbUnGtgCmHiMlUtNVexcE7OtCIZoVAlCGKNu7tyuJf10 +Qlk8oIIzfSIlcbHpOYoN79FkLoDNc2er4Gd+7w1oPQmdAB0jBJnA6t0OUBPKdDdE +Ufff2jrbfbzECn1ELg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +fejbXelUSJSzDIdY +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGCTCCA/GgAwIBAgIRAN7Y9G9i4I+ZaslPobE7VL4wDQYJKoZIhvcNAQEMBQAw +gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws +QW1hem9uIFJEUyBhcC1ub3J0aGVhc3QtMiBSb290IENBIFJTQTQwOTYgRzExEDAO +BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTIwMTYzMzIzWhgPMjEyMTA1MjAxNzMzMjNa +MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg +SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM +LEFtYXpvbiBSRFMgYXAtbm9ydGhlYXN0LTIgUm9vdCBDQSBSU0E0MDk2IEcxMRAw +DgYDVQQHDAdTZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +4BEPCiIfiK66Q/qa8k+eqf1Q3qsa6Xuu/fPkpuStXVBShhtXd3eqrM0iT4Xxs420 +Va0vSB3oZ7l86P9zYfa60n6PzRxdYFckYX330aI7L/oFIdaodB/C9szvROI0oLG+ +6RwmIF2zcprH0cTby8MiM7G3v9ykpq27g4WhDC1if2j8giOQL3oHpUaByekZNIHF +dIllsI3RkXmR3xmmxoOxJM1B9MZi7e1CvuVtTGOnSGpNCQiqofehTGwxCN2wFSK8 +xysaWlw48G0VzZs7cbxoXMH9QbMpb4tpk0d+T8JfAPu6uWO9UwCLWWydf0CkmA/+ +D50/xd1t33X9P4FEaPSg5lYbHXzSLWn7oLbrN2UqMLaQrkoEBg/VGvzmfN0mbflw ++T87bJ/VEOVNlG+gepyCTf89qIQVWOjuYMox4sK0PjzZGsYEuYiq1+OUT3vk/e5K +ag1fCcq2Isy4/iwB2xcXrsQ6ljwdk1fc+EmOnjGKrhuOHJY3S+RFv4ToQBsVyYhC +XGaC3EkqIX0xaCpDimxYhFjWhpDXAjG/zJ+hRLDAMCMhl/LPGRk/D1kzSbPmdjpl +lEMK5695PeBvEBTQdBQdOiYgOU3vWU6tzwwHfiM2/wgvess/q0FDAHfJhppbgbb9 +3vgsIUcsvoC5o29JvMsUxsDRvsAfEmMSDGkJoA/X6GECAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUgEWm1mZCbGD6ytbwk2UU1aLaOUUwDgYDVR0P +AQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQBb4+ABTGBGwxK1U/q4g8JDqTQM +1Wh8Oz8yAk4XtPJMAmCctxbd81cRnSnePWw/hxViLVtkZ/GsemvXfqAQyOn1coN7 +QeYSw+ZOlu0j2jEJVynmgsR7nIRqE7QkCyZAU+d2FTJUfmee+IiBiGyFGgxz9n7A +JhBZ/eahBbiuoOik/APW2JWLh0xp0W0GznfJ8lAlaQTyDa8iDXmVtbJg9P9qzkvl +FgPXQttzEOyooF8Pb2LCZO4kUz+1sbU7tHdr2YE+SXxt6D3SBv+Yf0FlvyWLiqVk +GDEOlPPTDSjAWgKnqST8UJ0RDcZK/v1ixs7ayqQJU0GUQm1I7LGTErWXHMnCuHKe +UKYuiSZwmTcJ06NgdhcCnGZgPq13ryMDqxPeltQc3n5eO7f1cL9ERYLDLOzm6A9P +oQ3MfcVOsbHgGHZWaPSeNrQRN9xefqBXH0ZPasgcH9WJdsLlEjVUXoultaHOKx3b +UCCb+d3EfqF6pRT488ippOL6bk7zNubwhRa/+y4wjZtwe3kAX78ACJVcjPobH9jZ +ErySads5zdQeaoee5wRKdp3TOfvuCe4bwLRdhOLCHWzEcXzY3g/6+ppLvNom8o+h +Bh5X26G6KSfr9tqhQ3O9IcbARjnuPbvtJnoPY0gz3EHHGPhy0RNW8i2gl3nUp0ah +PtjwbKW0hYAhIttT0Q== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICtzCCAj2gAwIBAgIQQRBQTs6Y3H1DDbpHGta3lzAKBggqhkjOPQQDAzCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIGFwLXNvdXRoZWFzdC0zIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDYxMTAwMTI0M1oYDzIxMjEwNjExMDExMjQzWjCBmzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6 +b24gUkRTIGFwLXNvdXRoZWFzdC0zIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEs0942Xj4m/gKA+WA6F5h +AHYuek9eGpzTRoLJddM4rEV1T3eSueytMVKOSlS3Ub9IhyQrH2D8EHsLYk9ktnGR +pATk0kCYTqFbB7onNo070lmMJmGT/Q7NgwC8cySChFxbo0IwQDAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBQ20iKBKiNkcbIZRu0y1uoF1yJTEzAOBgNVHQ8BAf8E +BAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIwYv0wTSrpQTaPaarfLN8Xcqrqu3hzl07n +FrESIoRw6Cx77ZscFi2/MV6AFyjCV/TlAjEAhpwJ3tpzPXpThRML8DMJYZ3YgMh3 +CMuLqhPpla3cL0PhybrD27hJWl29C4el6aMO +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrDCCAjOgAwIBAgIQGcztRyV40pyMKbNeSN+vXTAKBggqhkjOPQQDAzCBljEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMS8wLQYDVQQDDCZBbWF6 +b24gUkRTIHVzLWVhc3QtMiBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2Vh +dHRsZTAgFw0yMTA1MjEyMzE1NTZaGA8yMTIxMDUyMjAwMTU1NlowgZYxCzAJBgNV +BAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYD +VQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1hem9uIFJE +UyB1cy1lYXN0LTIgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1NlYXR0bGUw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQfDcv+GGRESD9wT+I5YIPRsD3L+/jsiIis +Tr7t9RSbFl+gYpO7ZbDXvNbV5UGOC5lMJo/SnqFRTC6vL06NF7qOHfig3XO8QnQz +6T5uhhrhnX2RSY3/10d2kTyHq3ZZg3+jQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFLDyD3PRyNXpvKHPYYxjHXWOgfPnMA4GA1UdDwEB/wQEAwIBhjAKBggq +hkjOPQQDAwNnADBkAjB20HQp6YL7CqYD82KaLGzgw305aUKw2aMrdkBR29J183jY +6Ocj9+Wcif9xnRMS+7oCMAvrt03rbh4SU9BohpRUcQ2Pjkh7RoY0jDR4Xq4qzjNr +5UFr3BXpFvACxXF51BksGQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQeKbS5zvtqDvRtwr5H48cAjAKBggqhkjOPQQDAzCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIG1lLXNvdXRoLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjEwNTIwMTcxOTU1WhgPMjEyMTA1MjAxODE5NTVaMIGXMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS +RFMgbWUtc291dGgtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs +ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABEKjgUaAPmUlRMEQdBC7BScAGosJ1zRV +LDd38qTBjzgmwBfQJ5ZfGIvyEK5unB09MB4e/3qqK5I/L6Qn5Px/n5g4dq0c7MQZ +u7G9GBYm90U3WRJBf7lQrPStXaRnS4A/O6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUNKcAbGEIn03/vkwd8g6jNyiRdD4wDgYDVR0PAQH/BAQDAgGGMAoG +CCqGSM49BAMDA2cAMGQCMHIeTrjenCSYuGC6txuBt/0ZwnM/ciO9kHGWVCoK8QLs +jGghb5/YSFGZbmQ6qpGlSAIwVOQgdFfTpEfe5i+Vs9frLJ4QKAfc27cTNYzRIM0I +E+AJgK4C4+DiyyMzOpiCfmvq +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +WT1QcNdw7ntUJP/w +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrzCCAjWgAwIBAgIQARky6+5PNFRkFVOp3Ob1CTAKBggqhkjOPQQDAzCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIGV1LXNvdXRoLTIgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjIwNTIzMTg0MTI4WhgPMjEyMjA1MjMxOTQxMjdaMIGXMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS +RFMgZXUtc291dGgtMiBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs +ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABNVGL5oF7cfIBxKyWd2PVK/S5yQfaJY3 +QFHWvEdt6951n9JhiiPrHzfVHsxZp1CBjILRMzjgRbYWmc8qRoLkgGE7htGdwudJ +Fa/WuKzO574Prv4iZXUnVGTboC7JdvKbh6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUgDeIIEKynwUbNXApdIPnmRWieZwwDgYDVR0PAQH/BAQDAgGGMAoG +CCqGSM49BAMDA2gAMGUCMEOOJfucrST+FxuqJkMZyCM3gWGZaB+/w6+XUAJC6hFM +uSTY0F44/bERkA4XhH+YGAIxAIpJQBakCA1/mXjsTnQ+0El9ty+LODp8ibkn031c +8DKDS7pR9UK7ZYdR6zFg3ZCjQw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjOgAwIBAgIQJvkWUcYLbnxtuwnyjMmntDAKBggqhkjOPQQDAzCBljEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMS8wLQYDVQQDDCZBbWF6 +b24gUkRTIGV1LXdlc3QtMyBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2Vh +dHRsZTAgFw0yMTA1MjUyMjI2MTJaGA8yMTIxMDUyNTIzMjYxMlowgZYxCzAJBgNV +BAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYD +VQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1hem9uIFJE +UyBldS13ZXN0LTMgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1NlYXR0bGUw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAARENn8uHCyjn1dFax4OeXxvbV861qsXFD9G +DshumTmFzWWHN/69WN/AOsxy9XN5S7Cgad4gQgeYYYgZ5taw+tFo/jQvCLY//uR5 +uihcLuLJ78opvRPvD9kbWZ6oXfBtFkWjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFKiK3LpoF+gDnqPldGSwChBPCYciMA4GA1UdDwEB/wQEAwIBhjAKBggq +hkjOPQQDAwNpADBmAjEA+7qfvRlnvF1Aosyp9HzxxCbN7VKu+QXXPhLEBWa5oeWW +UOcifunf/IVLC4/FGCsLAjEAte1AYp+iJyOHDB8UYkhBE/1sxnFaTiEPbvQBU0wZ +SuwWVLhu2wWDuSW+K7tTuL8p +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID/zCCAuegAwIBAgIRAKeDpqX5WFCGNo94M4v69sUwDQYJKoZIhvcNAQELBQAw +gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn +QW1hem9uIFJEUyBldS13ZXN0LTMgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUyNTIyMTgzM1oYDzIwNjEwNTI1MjMxODMzWjCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIGV1LXdlc3QtMyBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl +YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcKOTEMTfzvs4H +WtJR8gI7GXN6xesulWtZPv21oT+fLGwJ+9Bv8ADCGDDrDxfeH/HxJmzG9hgVAzVn +4g97Bn7q07tGZM5pVi96/aNp11velZT7spOJKfJDZTlGns6DPdHmx48whpdO+dOb +6+eR0VwCIv+Vl1fWXgoACXYCoKjhxJs+R+fwY//0JJ1YG8yjZ+ghLCJmvlkOJmE1 +TCPUyIENaEONd6T+FHGLVYRRxC2cPO65Jc4yQjsXvvQypoGgx7FwD5voNJnFMdyY +754JGPOOe/SZdepN7Tz7UEq8kn7NQSbhmCsgA/Hkjkchz96qN/YJ+H/okiQUTNB0 +eG9ogiVFAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFjayw9Y +MjbxfF14XAhMM2VPl0PfMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC +AQEAAtmx6d9+9CWlMoU0JCirtp4dSS41bBfb9Oor6GQ8WIr2LdfZLL6uES/ubJPE +1Sh5Vu/Zon5/MbqLMVrfniv3UpQIof37jKXsjZJFE1JVD/qQfRzG8AlBkYgHNEiS +VtD4lFxERmaCkY1tjKB4Dbd5hfhdrDy29618ZjbSP7NwAfnwb96jobCmMKgxVGiH +UqsLSiEBZ33b2hI7PJ6iTJnYBWGuiDnsWzKRmheA4nxwbmcQSfjbrNwa93w3caL2 +v/4u54Kcasvcu3yFsUwJygt8z43jsGAemNZsS7GWESxVVlW93MJRn6M+MMakkl9L +tWaXdHZ+KUV7LhfYLb0ajvb40w== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEBDCCAuygAwIBAgIQJ5oxPEjefCsaESSwrxk68DANBgkqhkiG9w0BAQsFADCB +mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB +bWF6b24gUkRTIGV1LWNlbnRyYWwtMiBSb290IENBIFJTQTIwNDggRzExEDAOBgNV +BAcMB1NlYXR0bGUwIBcNMjIwNjA2MjExNzA1WhgPMjA2MjA2MDYyMjE3MDVaMIGa +MQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5j +LjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMzAxBgNVBAMMKkFt +YXpvbiBSRFMgZXUtY2VudHJhbC0yIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UE +BwwHU2VhdHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALTQt5eX +g+VP3BjO9VBkWJhE0GfLrU/QIk32I6WvrnejayTrlup9H1z4QWlXF7GNJrqScRMY +KhJHlcP05aPsx1lYco6pdFOf42ybXyWHHJdShj4A5glU81GTT+VrXGzHSarLmtua +eozkQgPpDsSlPt0RefyTyel7r3Cq+5K/4vyjCTcIqbfgaGwTU36ffjM1LaPCuE4O +nINMeD6YuImt2hU/mFl20FZ+IZQUIFZZU7pxGLqTRz/PWcH8tDDxnkYg7tNuXOeN +JbTpXrw7St50/E9ZQ0llGS+MxJD8jGRAa/oL4G/cwnV8P2OEPVVkgN9xDDQeieo0 +3xkzolkDkmeKOnUCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +bwu8635iQGQMRanekesORM8Hkm4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQAgN6LE9mUgjsj6xGCX1afYE69fnmCjjb0rC6eEe1mb/QZNcyw4XBIW +6+zTXo4mjZ4ffoxb//R0/+vdTE7IvaLgfAZgFsLKJCtYDDstXZj8ujQnGR9Pig3R +W+LpNacvOOSJSawNQq0Xrlcu55AU4buyD5VjcICnfF1dqBMnGTnh27m/scd/ZMx/ +kapHZ/fMoK2mAgSX/NvUKF3UkhT85vSSM2BTtET33DzCPDQTZQYxFBa4rFRmFi4c +BLlmIReiCGyh3eJhuUUuYAbK6wLaRyPsyEcIOLMQmZe1+gAFm1+1/q5Ke9ugBmjf +PbTWjsi/lfZ5CdVAhc5lmZj/l5aKqwaS +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjSgAwIBAgIRAKKPTYKln9L4NTx9dpZGUjowCgYIKoZIzj0EAwMwgZYx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h +em9uIFJEUyBldS13ZXN0LTIgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjEwNTIxMjI1NTIxWhgPMjEyMTA1MjEyMzU1MjFaMIGWMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS +RFMgZXUtd2VzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE/owTReDvaRqdmbtTzXbyRmEpKCETNj6O +hZMKH0F8oU9Tmn8RU7kQQj6xUKEyjLPrFBN7c+26TvrVO1KmJAvbc8bVliiJZMbc +C0yV5PtJTalvlMZA1NnciZuhxaxrzlK1o0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G +A1UdDgQWBBT4i5HaoHtrs7Mi8auLhMbKM1XevDAOBgNVHQ8BAf8EBAMCAYYwCgYI +KoZIzj0EAwMDaAAwZQIxAK9A+8/lFdX4XJKgfP+ZLy5ySXC2E0Spoy12Gv2GdUEZ +p1G7c1KbWVlyb1d6subzkQIwKyH0Naf/3usWfftkmq8SzagicKz5cGcEUaULq4tO +GzA/AMpr63IDBAqkZbMDTCmH +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrzCCAjWgAwIBAgIQTgIvwTDuNWQo0Oe1sOPQEzAKBggqhkjOPQQDAzCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIGV1LW5vcnRoLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjEwNTI0MjEwNjM4WhgPMjEyMTA1MjQyMjA2MzhaMIGXMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS +RFMgZXUtbm9ydGgtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs +ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJuzXLU8q6WwSKXBvx8BbdIi3mPhb7Xo +rNJBfuMW1XRj5BcKH1ZoGaDGw+BIIwyBJg8qNmCK8kqIb4cH8/Hbo3Y+xBJyoXq/ +cuk8aPrxiNoRsKWwiDHCsVxaK9L7GhHHAqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUYgcsdU4fm5xtuqLNppkfTHM2QMYwDgYDVR0PAQH/BAQDAgGGMAoG +CCqGSM49BAMDA2gAMGUCMQDz/Rm89+QJOWJecYAmYcBWCcETASyoK1kbr4vw7Hsg +7Ew3LpLeq4IRmTyuiTMl0gMCMAa0QSjfAnxBKGhAnYxcNJSntUyyMpaXzur43ec0 +3D8npJghwC4DuICtKEkQiI5cSg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +u3UBpZI= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICtDCCAjmgAwIBAgIQenQbcP/Zbj9JxvZ+jXbRnTAKBggqhkjOPQQDAzCBmTEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTIwMAYDVQQDDClBbWF6 +b24gUkRTIGV1LWNlbnRyYWwtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwH +U2VhdHRsZTAgFw0yMTA1MjEyMjMzMjRaGA8yMTIxMDUyMTIzMzMyNFowgZkxCzAJ +BgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMw +EQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEyMDAGA1UEAwwpQW1hem9u +IFJEUyBldS1jZW50cmFsLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATlBHiEM9LoEb1Hdnd5j2VpCDOU +5nGuFoBD8ROUCkFLFh5mHrHfPXwBc63heW9WrP3qnDEm+UZEUvW7ROvtWCTPZdLz +Z4XaqgAlSqeE2VfUyZOZzBSgUUJk7OlznXfkCMOjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFDT/ThjQZl42Nv/4Z/7JYaPNMly2MA4GA1UdDwEB/wQEAwIB +hjAKBggqhkjOPQQDAwNpADBmAjEAnZWmSgpEbmq+oiCa13l5aGmxSlfp9h12Orvw +Dq/W5cENJz891QD0ufOsic5oGq1JAjEAp5kSJj0MxJBTHQze1Aa9gG4sjHBxXn98 +4MP1VGsQuhfndNHQb4V0Au7OWnOeiobq +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID/zCCAuegAwIBAgIRAMgnyikWz46xY6yRgiYwZ3swDQYJKoZIhvcNAQELBQAw +gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn +QW1hem9uIFJEUyBldS13ZXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUyMDE2NDkxMloYDzIwNjEwNTIwMTc0OTEyWjCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIGV1LXdlc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl +YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCi8JYOc9cYSgZH +gYPxLk6Xcc7HqzamvsnjYU98Dcb98y6iDqS46Ra2Ne02MITtU5MDL+qjxb8WGDZV +RUA9ZS69tkTO3gldW8QdiSh3J6hVNJQW81F0M7ZWgV0gB3n76WCmfT4IWos0AXHM +5v7M/M4tqVmCPViQnZb2kdVlM3/Xc9GInfSMCgNfwHPTXl+PXX+xCdNBePaP/A5C +5S0oK3HiXaKGQAy3K7VnaQaYdiv32XUatlM4K2WS4AMKt+2cw3hTCjlmqKRHvYFQ +veWCXAuc+U5PQDJ9SuxB1buFJZhT4VP3JagOuZbh5NWpIbOTxlAJOb5pGEDuJTKi +1gQQQVEFAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNXm+N87 +OFxK9Af/bjSxDCiulGUzMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC +AQEAkqIbkgZ45spvrgRQ6n9VKzDLvNg+WciLtmVrqyohwwJbj4pYvWwnKQCkVc7c +hUOSBmlSBa5REAPbH5o8bdt00FPRrD6BdXLXhaECKgjsHe1WW08nsequRKD8xVmc +8bEX6sw/utBeBV3mB+3Zv7ejYAbDFM4vnRsWtO+XqgReOgrl+cwdA6SNQT9oW3e5 +rSQ+VaXgJtl9NhkiIysq9BeYigxqS/A13pHQp0COMwS8nz+kBPHhJTsajHCDc8F4 +HfLi6cgs9G0gaRhT8FCH66OdGSqn196sE7Y3bPFFFs/3U+vxvmQgoZC6jegQXAg5 +Prxd+VNXtNI/azitTysQPumH7A== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEBTCCAu2gAwIBAgIRAO8bekN7rUReuNPG8pSTKtEwDQYJKoZIhvcNAQELBQAw +gZoxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEzMDEGA1UEAwwq +QW1hem9uIFJEUyBldS1jZW50cmFsLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYD +VQQHDAdTZWF0dGxlMCAXDTIxMDUyMTIyMjM0N1oYDzIwNjEwNTIxMjMyMzQ3WjCB +mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB +bWF6b24gUkRTIGV1LWNlbnRyYWwtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNV +BAcMB1NlYXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCTTYds +Tray+Q9VA5j5jTh5TunHKFQzn68ZbOzdqaoi/Rq4ohfC0xdLrxCpfqn2TGDHN6Zi +2qGK1tWJZEd1H0trhzd9d1CtGK+3cjabUmz/TjSW/qBar7e9MA67/iJ74Gc+Ww43 +A0xPNIWcL4aLrHaLm7sHgAO2UCKsrBUpxErOAACERScVYwPAfu79xeFcX7DmcX+e +lIqY16pQAvK2RIzrekSYfLFxwFq2hnlgKHaVgZ3keKP+nmXcXmRSHQYUUr72oYNZ +HcNYl2+gxCc9ccPEHM7xncVEKmb5cWEWvVoaysgQ+osi5f5aQdzgC2X2g2daKbyA +XL/z5FM9GHpS5BJjAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FBDAiJ7Py9/A9etNa/ebOnx5l5MGMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0B +AQsFAAOCAQEALMh/+81fFPdJV/RrJUeoUvFCGMp8iaANu97NpeJyKitNOv7RoeVP +WjivS0KcCqZaDBs+p6IZ0sLI5ZH098LDzzytcfZg0PsGqUAb8a0MiU/LfgDCI9Ee +jsOiwaFB8k0tfUJK32NPcIoQYApTMT2e26lPzYORSkfuntme2PTHUnuC7ikiQrZk +P+SZjWgRuMcp09JfRXyAYWIuix4Gy0eZ4rpRuaTK6mjAb1/LYoNK/iZ/gTeIqrNt +l70OWRsWW8jEmSyNTIubGK/gGGyfuZGSyqoRX6OKHESkP6SSulbIZHyJ5VZkgtXo +2XvyRyJ7w5pFyoofrL3Wv0UF8yt/GDszmg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF/zCCA+egAwIBAgIRAMDk/F+rrhdn42SfE+ghPC8wDQYJKoZIhvcNAQEMBQAw +gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn +QW1hem9uIFJEUyBldS13ZXN0LTIgUm9vdCBDQSBSU0E0MDk2IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUyMTIyNTEyMloYDzIxMjEwNTIxMjM1MTIyWjCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIGV1LXdlc3QtMiBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2twMALVg9vRVu +VNqsr6N8thmp3Dy8jEGTsm3GCQ+C5P2YcGlD/T/5icfWW84uF7Sx3ezcGlvsqFMf +Ukj9sQyqtz7qfFFugyy7pa/eH9f48kWFHLbQYm9GEgbYBIrWMp1cy3vyxuMCwQN4 +DCncqU+yNpy0CprQJEha3PzY+3yJOjDQtc3zr99lyECCFJTDUucxHzyQvX89eL74 +uh8la0lKH3v9wPpnEoftbrwmm5jHNFdzj7uXUHUJ41N7af7z7QUfghIRhlBDiKtx +5lYZemPCXajTc3ryDKUZC/b+B6ViXZmAeMdmQoPE0jwyEp/uaUcdp+FlUQwCfsBk +ayPFEApTWgPiku2isjdeTVmEgL8bJTDUZ6FYFR7ZHcYAsDzcwHgIu3GGEMVRS3Uf +ILmioiyly9vcK4Sa01ondARmsi/I0s7pWpKflaekyv5boJKD/xqwz9lGejmJHelf +8Od2TyqJScMpB7Q8c2ROxBwqwB72jMCEvYigB+Wnbb8RipliqNflIGx938FRCzKL +UQUBmNAznR/yRRL0wHf9UAE/8v9a09uZABeiznzOFAl/frHpgdAbC00LkFlnwwgX +g8YfEFlkp4fLx5B7LtoO6uVNFVimLxtwirpyKoj3G4M/kvSTux8bTw0heBCmWmKR +57MS6k7ODzbv+Kpeht2hqVZCNFMxoQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBRuMnDhJjoj7DcKALj+HbxEqj3r6jAOBgNVHQ8BAf8EBAMCAYYw +DQYJKoZIhvcNAQEMBQADggIBALSnXfx72C3ldhBP5kY4Mo2DDaGQ8FGpTOOiD95d +0rf7I9LrsBGVqu/Nir+kqqP80PB70+Jy9fHFFigXwcPBX3MpKGxK8Cel7kVf8t1B +4YD6A6bqlzP+OUL0uGWfZpdpDxwMDI2Flt4NEldHgXWPjvN1VblEKs0+kPnKowyg +jhRMgBbD/y+8yg0fIcjXUDTAw/+INcp21gWaMukKQr/8HswqC1yoqW9in2ijQkpK +2RB9vcQ0/gXR0oJUbZQx0jn0OH8Agt7yfMAnJAdnHO4M3gjvlJLzIC5/4aGrRXZl +JoZKfJ2fZRnrFMi0nhAYDeInoS+Rwx+QzaBk6fX5VPyCj8foZ0nmqvuYoydzD8W5 +mMlycgxFqS+DUmO+liWllQC4/MnVBlHGB1Cu3wTj5kgOvNs/k+FW3GXGzD3+rpv0 +QTLuwSbMr+MbEThxrSZRSXTCQzKfehyC+WZejgLb+8ylLJUA10e62o7H9PvCrwj+ +ZDVmN7qj6amzvndCP98sZfX7CFZPLfcBd4wVIjHsFjSNEwWHOiFyLPPG7cdolGKA +lOFvonvo4A1uRc13/zFeP0Xi5n5OZ2go8aOOeGYdI2vB2sgH9R2IASH/jHmr0gvY +0dfBCcfXNgrS0toq0LX/y+5KkKOxh52vEYsJLdhqrveuZhQnsFEm/mFwjRXkyO7c +2jpC +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +FEv9Bg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICsDCCAjagAwIBAgIRAMgApnfGYPpK/fD0dbN2U4YwCgYIKoZIzj0EAwMwgZcx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwnQW1h +em9uIFJEUyBldS1zb3V0aC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdT +ZWF0dGxlMCAXDTIxMDUxOTE4MzgxMVoYDzIxMjEwNTE5MTkzODExWjCBlzELMAkG +A1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzAR +BgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6b24g +UkRTIGV1LXNvdXRoLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1NlYXR0 +bGUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQfEWl6d4qSuIoECdZPp+39LaKsfsX7 +THs3/RrtT0+h/jl3bjZ7Qc68k16x+HGcHbaayHfqD0LPdzH/kKtNSfQKqemdxDQh +Z4pwkixJu8T1VpXZ5zzCvBXCl75UqgEFS92jQjBAMA8GA1UdEwEB/wQFMAMBAf8w +HQYDVR0OBBYEFFPrSNtWS5JU+Tvi6ABV231XbjbEMA4GA1UdDwEB/wQEAwIBhjAK +BggqhkjOPQQDAwNoADBlAjEA+a7hF1IrNkBd2N/l7IQYAQw8chnRZDzh4wiGsZsC +6A83maaKFWUKIb3qZYXFSi02AjAbp3wxH3myAmF8WekDHhKcC2zDvyOiKLkg9Y6v +ZVmyMR043dscQbcsVoacOYv198c= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICtDCCAjqgAwIBAgIRAPhVkIsQ51JFhD2kjFK5uAkwCgYIKoZIzj0EAwMwgZkx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEyMDAGA1UEAwwpQW1h +em9uIFJEUyBldS1jZW50cmFsLTIgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcM +B1NlYXR0bGUwIBcNMjIwNjA2MjEyOTE3WhgPMjEyMjA2MDYyMjI5MTdaMIGZMQsw +CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET +MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMjAwBgNVBAMMKUFtYXpv +biBSRFMgZXUtY2VudHJhbC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdT +ZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEA5xnIEBtG5b2nmbj49UEwQza +yX0844fXjccYzZ8xCDUe9dS2XOUi0aZlGblgSe/3lwjg8fMcKXLObGGQfgIx1+5h +AIBjORis/dlyN5q/yH4U5sjS8tcR0GDGVHrsRUZCo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBRK+lSGutXf4DkTjR3WNfv4+KeNFTAOBgNVHQ8BAf8EBAMC +AYYwCgYIKoZIzj0EAwMDaAAwZQIxAJ4NxQ1Gerqr70ZrnUqc62Vl8NNqTzInamCG +Kce3FTsMWbS9qkgrjZkO9QqOcGIw/gIwSLrwUT+PKr9+H9eHyGvpq9/3AIYSnFkb +Cf3dyWPiLKoAtLFwjzB/CkJlsAS1c8dS +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF/jCCA+agAwIBAgIQGZH12Q7x41qIh9vDu9ikTjANBgkqhkiG9w0BAQwFADCB +lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB +bWF6b24gUkRTIGV1LXdlc3QtMyBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcM +B1NlYXR0bGUwIBcNMjEwNTI1MjIyMjMzWhgPMjEyMTA1MjUyMzIyMzNaMIGXMQsw +CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET +MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv +biBSRFMgZXUtd2VzdC0zIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwHU2Vh +dHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMqE47sHXWzdpuqj +JHb+6jM9tDbQLDFnYjDWpq4VpLPZhb7xPNh9gnYYTPKG4avG421EblAHqzy9D2pN +1z90yKbIfUb/Sy2MhQbmZomsObhONEra06fJ0Dydyjswf1iYRp2kwpx5AgkVoNo7 +3dlws73zFjD7ImKvUx2C7B75bhnw2pJWkFnGcswl8fZt9B5Yt95sFOKEz2MSJE91 +kZlHtya19OUxZ/cSGci4MlOySzqzbGwUqGxEIDlY8I39VMwXaYQ8uXUN4G780VcL +u46FeyRGxZGz2n3hMc805WAA1V5uir87vuirTvoSVREET97HVRGVVNJJ/FM6GXr1 +VKtptybbo81nefYJg9KBysxAa2Ao2x2ry/2ZxwhS6VZ6v1+90bpZA1BIYFEDXXn/ +dW07HSCFnYSlgPtSc+Muh15mdr94LspYeDqNIierK9i4tB6ep7llJAnq0BU91fM2 +JPeqyoTtc3m06QhLf68ccSxO4l8Hmq9kLSHO7UXgtdjfRVaffngopTNk8qK7bIb7 +LrgkqhiQw/PRCZjUdyXL153/fUcsj9nFNe25gM4vcFYwH6c5trd2tUl31NTi1MfG +Mgp3d2dqxQBIYANkEjtBDMy3SqQLIo9EymqmVP8xx2A/gCBgaxvMAsI6FSWRoC7+ +hqJ8XH4mFnXSHKtYMe6WPY+/XZgtAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8w +HQYDVR0OBBYEFIkXqTnllT/VJnI2NqipA4XV8rh1MA4GA1UdDwEB/wQEAwIBhjAN +BgkqhkiG9w0BAQwFAAOCAgEAKjSle8eenGeHgT8pltWCw/HzWyQruVKhfYIBfKJd +MhV4EnH5BK7LxBIvpXGsFUrb0ThzSw0fn0zoA9jBs3i/Sj6KyeZ9qUF6b8ycDXd+ +wHonmJiQ7nk7UuMefaYAfs06vosgl1rI7eBHC0itexIQmKh0aX+821l4GEgEoSMf +loMFTLXv2w36fPHHCsZ67ODldgcZbKNnpCTX0YrCwEYO3Pz/L398btiRcWGrewrK +jdxAAyietra8DRno1Zl87685tfqc6HsL9v8rVw58clAo9XAQvT+fmSOFw/PogRZ7 +OMHUat3gu/uQ1M5S64nkLLFsKu7jzudBuoNmcJysPlzIbqJ7vYc82OUGe9ucF3wi +3tbKQ983hdJiTExVRBLX/fYjPsGbG3JtPTv89eg2tjWHlPhCDMMxyRKl6isu2RTq +6VT489Z2zQrC33MYF8ZqO1NKjtyMAMIZwxVu4cGLkVsqFmEV2ScDHa5RadDyD3Ok +m+mqybhvEVm5tPgY6p0ILPMN3yvJsMSPSvuBXhO/X5ppNnpw9gnxpwbjQKNhkFaG +M5pkADZ14uRguOLM4VthSwUSEAr5VQYCFZhEwK+UOyJAGiB/nJz6IxL5XBNUXmRM +Hl8Xvz4riq48LMQbjcVQj0XvH941yPh+P8xOi00SGaQRaWp55Vyr4YKGbV0mEDz1 +r1o= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF/zCCA+egAwIBAgIRAKwYju1QWxUZpn6D1gOtwgQwDQYJKoZIhvcNAQEMBQAw +gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ +bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn +QW1hem9uIFJEUyBldS13ZXN0LTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUyMDE2NTM1NFoYDzIxMjEwNTIwMTc1MzU0WjCBlzEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6 +b24gUkRTIGV1LXdlc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCKdBP1U4lqWWkc +Cb25/BKRTsvNVnISiKocva8GAzJyKfcGRa85gmgu41U+Hz6+39K+XkRfM0YS4BvQ +F1XxWT0bNyypuvwCvmYShSTjN1TY0ltncDddahTajE/4MdSOZb/c98u0yt03cH+G +hVwRyT50h0v/UEol50VfwcVAEZEgcQQYhf1IFUFlIvKpmDOqLuFakOnc7c9akK+i +ivST+JO1tgowbnNkn2iLlSSgUWgb1gjaOsNfysagv1RXdlyPw3EyfwkFifAQvF2P +Q0ayYZfYS640cccv7efM1MSVyFHR9PrrDsF/zr2S2sGPbeHr7R/HwLl+S5J/l9N9 +y0rk6IHAWV4dEkOvgpnuJKURwA48iu1Hhi9e4moNS6eqoK2KmY3VFpuiyWcA73nH +GSmyaH+YuMrF7Fnuu7GEHZL/o6+F5cL3mj2SJJhL7sz0ryf5Cs5R4yN9BIEj/f49 +wh84pM6nexoI0Q4wiSFCxWiBpjSmOK6h7z6+2utaB5p20XDZHhxAlmlx4vMuWtjh +XckgRFxc+ZpVMU3cAHUpVEoO49e/+qKEpPzp8Xg4cToKw2+AfTk3cmyyXQfGwXMQ +ZUHNZ3w9ILMWihGCM2aGUsLcGDRennvNmnmin/SENsOQ8Ku0/a3teEzwV9cmmdYz +5iYs1YtgPvKFobY6+T2RXXh+A5kprwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBSyUrsQVnKmA8z6/2Ech0rCvqpNmTAOBgNVHQ8BAf8EBAMCAYYw +DQYJKoZIhvcNAQEMBQADggIBAFlj3IFmgiFz5lvTzFTRizhVofhTJsGr14Yfkuc7 +UrXPuXOwJomd4uot2d/VIeGJpfnuS84qGdmQyGewGTJ9inatHsGZgHl9NHNWRwKZ +lTKTbBiq7aqgtUSFa06v202wpzU+1kadxJJePrbABxiXVfOmIW/a1a4hPNcT3syH +FIEg1+CGsp71UNjBuwg3JTKWna0sLSKcxLOSOvX1fzxK5djzVpEsvQMB4PSAzXca +vENgg2ErTwgTA+4s6rRtiBF9pAusN1QVuBahYP3ftrY6f3ycS4K65GnqscyfvKt5 +YgjtEKO3ZeeX8NpubMbzC+0Z6tVKfPFk/9TXuJtwvVeqow0YMrLLyRiYvK7EzJ97 +rrkxoKnHYQSZ+rH2tZ5SE392/rfk1PJL0cdHnkpDkUDO+8cKsFjjYKAQSNC52sKX +74AVh6wMwxYwVZZJf2/2XxkjMWWhKNejsZhUkTISSmiLs+qPe3L67IM7GyKm9/m6 +R3r8x6NGjhTsKH64iYJg7AeKeax4b2e4hBb6GXFftyOs7unpEOIVkJJgM6gh3mwn +R7v4gwFbLKADKt1vHuerSZMiTuNTGhSfCeDM53XI/mjZl2HeuCKP1mCDLlaO+gZR +Q/G+E0sBKgEX4xTkAc3kgkuQGfExdGtnN2U2ehF80lBHB8+2y2E+xWWXih/ZyIcW +wOx+ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGBDCCA+ygAwIBAgIQM4C8g5iFRucSWdC8EdqHeDANBgkqhkiG9w0BAQwFADCB +mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB +bWF6b24gUkRTIGV1LWNlbnRyYWwtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNV +BAcMB1NlYXR0bGUwIBcNMjEwNTIxMjIyODI2WhgPMjEyMTA1MjEyMzI4MjZaMIGa +MQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5j +LjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMzAxBgNVBAMMKkFt +YXpvbiBSRFMgZXUtY2VudHJhbC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UE +BwwHU2VhdHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANeTsD/u +6saPiY4Sg0GlJlMXMBltnrcGAEkwq34OKQ0bCXqcoNJ2rcAMmuFC5x9Ho1Y3YzB7 +NO2GpIh6bZaO76GzSv4cnimcv9n/sQSYXsGbPD+bAtnN/RvNW1avt4C0q0/ghgF1 +VFS8JihIrgPYIArAmDtGNEdl5PUrdi9y6QGggbRfidMDdxlRdZBe1C18ZdgERSEv +UgSTPRlVczONG5qcQkUGCH83MMqL5MKQiby/Br5ZyPq6rxQMwRnQ7tROuElzyYzL +7d6kke+PNzG1mYy4cbYdjebwANCtZ2qYRSUHAQsOgybRcSoarv2xqcjO9cEsDiRU +l97ToadGYa4VVERuTaNZxQwrld4mvzpyKuirqZltOqg0eoy8VUsaRPL3dc5aChR0 +dSrBgRYmSAClcR2/2ZCWpXemikwgt031Dsc0A/+TmVurrsqszwbr0e5xqMow9LzO +MI/JtLd0VFtoOkL/7GG2tN8a+7gnLFxpv+AQ0DH5n4k/BY/IyS+H1erqSJhOTQ11 +vDOFTM5YplB9hWV9fp5PRs54ILlHTlZLpWGs3I2BrJwzRtg/rOlvsosqcge9ryai +AKm2j+JBg5wJ19R8oxRy8cfrNTftZePpISaLTyV2B16w/GsSjqixjTQe9LRN2DHk +cC+HPqYyzW2a3pUVyTGHhW6a7YsPBs9yzt6hAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFIqA8QkOs2cSirOpCuKuOh9VDfJfMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQwFAAOCAgEAOUI90mEIsa+vNJku0iUwdBMnHiO4gm7E +5JloP7JG0xUr7d0hypDorMM3zVDAL+aZRHsq8n934Cywj7qEp1304UF6538ByGdz +tkfacJsUSYfdlNJE9KbA4T+U+7SNhj9jvePpVjdQbhgzxITE9f8CxY/eM40yluJJ +PhbaWvOiRagzo74wttlcDerzLT6Y/JrVpWhnB7IY8HvzK+BwAdaCsBUPC3HF+kth +CIqLq7J3YArTToejWZAp5OOI6DLPM1MEudyoejL02w0jq0CChmZ5i55ElEMnapRX +7GQTARHmjgAOqa95FjbHEZzRPqZ72AtZAWKFcYFNk+grXSeWiDgPFOsq6mDg8DDB +0kfbYwKLFFCC9YFmYzR2YrWw2NxAScccUc2chOWAoSNHiqBbHR8ofrlJSWrtmKqd +YRCXzn8wqXnTS3NNHNccqJ6dN+iMr9NGnytw8zwwSchiev53Fpc1mGrJ7BKTWH0t +ZrA6m32wzpMymtKozlOPYoE5mtZEzrzHEXfa44Rns7XIHxVQSXVWyBHLtIsZOrvW +U5F41rQaFEpEeUQ7sQvqUoISfTUVRNDn6GK6YaccEhCji14APLFIvhRQUDyYMIiM +4vll0F/xgVRHTgDVQ8b8sxdhSYlqB4Wc2Ym41YRz+X2yPqk3typEZBpc4P5Tt1/N +89cEIGdbjsA= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIQYjbPSg4+RNRD3zNxO1fuKDANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB +bWF6b24gUkRTIGV1LW5vcnRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUyNDIwNTkyMVoYDzIwNjEwNTI0MjE1OTIxWjCBmDEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6 +b24gUkRTIGV1LW5vcnRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT +ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA179eQHxcV0YL +XMkqEmhSBazHhnRVd8yICbMq82PitE3BZcnv1Z5Zs/oOgNmMkOKae4tCXO/41JCX +wAgbs/eWWi+nnCfpQ/FqbLPg0h3dqzAgeszQyNl9IzTzX4Nd7JFRBVJXPIIKzlRf ++GmFsAhi3rYgDgO27pz3ciahVSN+CuACIRYnA0K0s9lhYdddmrW/SYeWyoB7jPa2 +LmWpAs7bDOgS4LlP2H3eFepBPgNufRytSQUVA8f58lsE5w25vNiUSnrdlvDrIU5n +Qwzc7NIZCx4qJpRbSKWrUtbyJriWfAkGU7i0IoainHLn0eHp9bWkwb9D+C/tMk1X +ERZw2PDGkwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSFmR7s +dAblusFN+xhf1ae0KUqhWTAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggEBAHsXOpjPMyH9lDhPM61zYdja1ebcMVgfUvsDvt+w0xKMKPhBzYDMs/cFOi1N +Q8LV79VNNfI2NuvFmGygcvTIR+4h0pqqZ+wjWl3Kk5jVxCrbHg3RBX02QLumKd/i +kwGcEtTUvTssn3SM8bgM0/1BDXgImZPC567ciLvWDo0s/Fe9dJJC3E0G7d/4s09n +OMdextcxFuWBZrBm/KK3QF0ByA8MG3//VXaGO9OIeeOJCpWn1G1PjT1UklYhkg61 +EbsTiZVA2DLd1BGzfU4o4M5mo68l0msse/ndR1nEY6IywwpgIFue7+rEleDh6b9d +PYkG1rHVw2I0XDG4o17aOn5E94I= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIQC6W4HFghUkkgyQw14a6JljANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB +bWF6b24gUkRTIGV1LXNvdXRoLTIgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIyMDUyMzE4MTYzMloYDzIwNjIwNTIzMTkxNjMyWjCBmDEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6 +b24gUkRTIGV1LXNvdXRoLTIgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT +ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiM/t4FV2R9Nx +UQG203UY83jInTa/6TMq0SPyg617FqYZxvz2kkx09x3dmxepUg9ttGMlPgjsRZM5 +LCFEi1FWk+hxHzt7vAdhHES5tdjwds3aIkgNEillmRDVrUsbrDwufLaa+MMDO2E1 +wQ/JYFXw16WBCCi2g1EtyQ2Xp+tZDX5IWOTnvhZpW8vVDptZ2AcJ5rMhfOYO3OsK +5EF0GGA5ldzuezP+BkrBYGJ4wVKGxeaq9+5AT8iVZrypjwRkD7Y5CurywK3+aBwm +s9Q5Nd8t45JCOUzYp92rFKsCriD86n/JnEvgDfdP6Hvtm0/DkwXK40Wz2q0Zrd0k +mjP054NRPwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRR7yqd +SfKcX2Q8GzhcVucReIpewTAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggEBAEszBRDwXcZyNm07VcFwI1Im94oKwKccuKYeJEsizTBsVon8VpEiMwDs+yGu +3p8kBhvkLwWybkD/vv6McH7T5b9jDX2DoOudqYnnaYeypsPH/00Vh3LvKagqzQza +orWLx+0tLo8xW4BtU+Wrn3JId8LvAhxyYXTn9bm+EwPcStp8xGLwu53OPD1RXYuy +uu+3ps/2piP7GVfou7H6PRaqbFHNfiGg6Y+WA0HGHiJzn8uLmrRJ5YRdIOOG9/xi +qTmAZloUNM7VNuurcMM2hWF494tQpsQ6ysg2qPjbBqzlGoOt3GfBTOZmqmwmqtam +K7juWM/mdMQAJ3SMlE5wI8nVdx4= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjSgAwIBAgIRAL9SdzVPcpq7GOpvdGoM80IwCgYIKoZIzj0EAwMwgZYx +CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu +MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h +em9uIFJEUyBldS13ZXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl +YXR0bGUwIBcNMjEwNTIwMTY1ODA3WhgPMjEyMTA1MjAxNzU4MDdaMIGWMQswCQYD +VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG +A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS +RFMgZXUtd2VzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEJWDgXebvwjR+Ce+hxKOLbnsfN5W5dOlP +Zn8kwWnD+SLkU81Eac/BDJsXGrMk6jFD1vg16PEkoSevsuYWlC8xR6FmT6F6pmeh +fsMGOyJpfK4fyoEPhKeQoT23lFIc5Orjo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G +A1UdDgQWBBSVNAN1CHAz0eZ77qz2adeqjm31TzAOBgNVHQ8BAf8EBAMCAYYwCgYI +KoZIzj0EAwMDaAAwZQIxAMlQeHbcjor49jqmcJ9gRLWdEWpXG8thIf6zfYQ/OEAg +d7GDh4fR/OUk0VfjsBUN/gIwZB0bGdXvK38s6AAE/9IT051cz/wMe9GIrX1MnL1T +1F5OqnXJdiwfZRRTHsRQ/L00 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +1YfpdH2jp30= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQTDc+UgTRtYO7ZGTQ8UWKDDANBgkqhkiG9w0BAQsFADCB +lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB +bWF6b24gUkRTIGV1LXdlc3QtMiBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcM +B1NlYXR0bGUwIBcNMjEwNTIxMjI0NjI0WhgPMjA2MTA1MjEyMzQ2MjRaMIGXMQsw +CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET +MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv +biBSRFMgZXUtd2VzdC0yIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UEBwwHU2Vh +dHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM1oGtthQ1YiVIC2 +i4u4swMAGxAjc/BZp0yq0eP5ZQFaxnxs7zFAPabEWsrjeDzrRhdVO0h7zskrertP +gblGhfD20JfjvCHdP1RUhy/nzG+T+hn6Takan/GIgs8grlBMRHMgBYHW7tklhjaH +3F7LujhceAHhhgp6IOrpb6YTaTTaJbF3GTmkqxSJ3l1LtEoWz8Al/nL/Ftzxrtez +Vs6ebpvd7sw37sxmXBWX2OlvUrPCTmladw9OrllGXtCFw4YyLe3zozBlZ3cHzQ0q +lINhpRcajTMfZrsiGCkQtoJT+AqVJPS2sHjqsEH8yiySW9Jbq4zyMbM1yqQ2vnnx +MJgoYMcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUaQG88UnV +JPTI+Pcti1P+q3H7pGYwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IB +AQBAkgr75V0sEJimC6QRiTVWEuj2Khy7unjSfudbM6zumhXEU2/sUaVLiYy6cA/x +3v0laDle6T07x9g64j5YastE/4jbzrGgIINFlY0JnaYmR3KZEjgi1s1fkRRf3llL +PJm9u4Q1mbwAMQK/ZjLuuRcL3uRIHJek18nRqT5h43GB26qXyvJqeYYpYfIjL9+/ +YiZAbSRRZG+Li23cmPWrbA1CJY121SB+WybCbysbOXzhD3Sl2KSZRwSw4p2HrFtV +1Prk0dOBtZxCG9luf87ultuDZpfS0w6oNBAMXocgswk24ylcADkkFxBWW+7BETn1 +EpK+t1Lm37mU4sxtuha00XAi +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIQcY44/8NUvBwr6LlHfRy7KjANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu +Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB +bWF6b24gUkRTIGV1LXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH +DAdTZWF0dGxlMCAXDTIxMDUxOTE4MjcxOFoYDzIwNjEwNTE5MTkyNzE4WjCBmDEL +MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x +EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6 +b24gUkRTIGV1LXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT +ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0UaBeC+Usalu +EtXnV7+PnH+gi7/71tI/jkKVGKuhD2JDVvqLVoqbMHRh3+wGMvqKCjbHPcC2XMWv +566fpAj4UZ9CLB5fVzss+QVNTl+FH2XhEzigopp+872ajsNzcZxrMkifxGb4i0U+ +t0Zi+UrbL5tsfP2JonKR1crOrbS6/DlzHBjIiJazGOQcMsJjNuTOItLbMohLpraA +/nApa3kOvI7Ufool1/34MG0+wL3UUA4YkZ6oBJVxjZvvs6tI7Lzz/SnhK2widGdc +snbLqBpHNIZQSorVoiwcFaRBGYX/uzYkiw44Yfa4cK2V/B5zgu1Fbr0gbI2am4eh +yVYyg4jPawIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS9gM1m +IIjyh9O5H/7Vj0R/akI7UzAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggEBAF0Sm9HC2AUyedBVnwgkVXMibnYChOzz7T+0Y+fOLXYAEXex2s8oqGeZdGYX +JHkjBn7JXu7LM+TpTbPbFFDoc1sgMguD/ls+8XsqAl1CssW+amryIL+jfcfbgQ+P +ICwEUD9hGdjBgJ5WcuS+qqxHsEIlFNci3HxcxfBa9VsWs5TjI7Vsl4meL5lf7ZyL +wDV7dHRuU+cImqG1MIvPRIlvPnT7EghrCYi2VCPhP2pM/UvShuwVnkz4MJ29ebIk +WR9kpblFxFdE92D5UUvMCjC2kmtgzNiErvTcwIvOO9YCbBHzRB1fFiWrXUHhJWq9 +IkaxR5icb/IpAV0A1lYZEWMVsfQ= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +rM2p0kk= +-----END CERTIFICATE----- diff --git a/backfill/go.mod b/backfill/go.mod new file mode 100644 index 00000000..caa412db --- /dev/null +++ b/backfill/go.mod @@ -0,0 +1,17 @@ +module go-backfill + +go 1.23.3 + +require ( + github.com/aws/aws-sdk-go v1.55.5 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.1 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/lib/pq v1.10.9 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/text v0.18.0 // indirect +) diff --git a/backfill/go.sum b/backfill/go.sum new file mode 100644 index 00000000..7ce24722 --- /dev/null +++ b/backfill/go.sum @@ -0,0 +1,31 @@ +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= +github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backfill/main.go b/backfill/main.go new file mode 100644 index 00000000..84aed365 --- /dev/null +++ b/backfill/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "flag" + "go-backfill/config" + "go-backfill/fetch" + "go-backfill/process" +) + +func main() { + envFile := flag.String("env", ".env", "Path to the .env file") + flag.Parse() + config.InitEnv(*envFile) + env := config.GetConfig() + + pool := config.InitDatabase() + defer pool.Close() + + go config.StartMemoryMonitoring() + cut := fetch.FetchCut() + ChainId := env.ChainId + SyncMinHeight := env.SyncMinHeight + process.StartBackfill(cut.Height, cut.Hash, ChainId, SyncMinHeight, pool) +} diff --git a/backfill/middle-backfill/middle-backfill.go b/backfill/middle-backfill/middle-backfill.go new file mode 100644 index 00000000..ca60d768 --- /dev/null +++ b/backfill/middle-backfill/middle-backfill.go @@ -0,0 +1,83 @@ +package main + +import ( + "context" + "flag" + "fmt" + "go-backfill/config" + "go-backfill/fetch" + "go-backfill/process" + "strconv" + + "github.com/jackc/pgx/v5/pgxpool" +) + +type BlockHeight struct { + ChainID int64 + MaxHeight int64 +} + +func main() { + envFile := flag.String("env", ".env", "Path to the .env file") + flag.Parse() + config.InitEnv(*envFile) + + pool := config.InitDatabase() + defer pool.Close() + + go config.StartMemoryMonitoring() + cuts := fetch.FetchCuts() + maxHeights, err := FetchMaxHeightByChain(pool) + if err != nil { + fmt.Printf("error fetching max heights: %v\n", err) + return + } + + for _, maxHeight := range maxHeights { + ChainIdStr := strconv.FormatInt(maxHeight.ChainID, 10) + Height := cuts.Hashes[ChainIdStr].Height + Hash := cuts.Hashes[ChainIdStr].Hash + // fmt.Println("Hash: ", Hash, " ChainId: ", ChainIdStr, " Height: ", Height, " MaxHeight: ", maxHeight.MaxHeight) + process.StartBackfill(Height, Hash, int(maxHeight.ChainID), int(maxHeight.MaxHeight+1), pool) + } +} + +func FetchMaxHeightByChain(pool *pgxpool.Pool) ([]BlockHeight, error) { + query := ` + SELECT MAX(b.height) AS max_height, b."chainId" + FROM "Blocks" b + GROUP BY b."chainId" + ORDER BY b."chainId" DESC; + ` + + rows, err := pool.Query(context.Background(), query) + if err != nil { + return nil, fmt.Errorf("failed to execute query: %w", err) + } + defer rows.Close() + + var results []BlockHeight + for rows.Next() { + var result BlockHeight + if err := rows.Scan(&result.MaxHeight, &result.ChainID); err != nil { + return nil, fmt.Errorf("failed to scan row: %w", err) + } + results = append(results, result) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error occurred during row iteration: %w", err) + } + + // const maxHeight = 5444446 + // var blockHeights = []BlockHeight{} + + // for i := 0; i < 20; i++ { + // blockHeights = append(blockHeights, BlockHeight{ + // ChainID: int64(i), + // MaxHeight: maxHeight, + // }) + // } + + return results, nil +} diff --git a/backfill/process/get_coin_transfers.go b/backfill/process/get_coin_transfers.go new file mode 100644 index 00000000..9d4d1f05 --- /dev/null +++ b/backfill/process/get_coin_transfers.go @@ -0,0 +1,55 @@ +package process + +import ( + "go-backfill/fetch" + "go-backfill/repository" + "log" +) + +func GetCoinTransfers( + events []fetch.Event, + chainId int, + requestKey string, + transactionId int64, +) []repository.TransferAttributes { + const TransferCoinSignature = "TRANSFER" + const TransferCoinParamsLength = 3 + + transfers := make([]repository.TransferAttributes, 0, len(events)) + + for _, event := range events { + if event.Name == TransferCoinSignature && len(event.Params) == TransferCoinParamsLength { + fromAcct, ok1 := event.Params[0].(string) + toAcct, ok2 := event.Params[1].(string) + amount, ok3 := convertToFloat64(event, 2) + + // Ensure all parameters are of the expected types + if !ok1 || !ok2 || !ok3 { + log.Printf("Skipping event due to invalid parameter types: %+v\n", requestKey) + continue + } + + moduleName := buildModuleName(event.Module.Namespace, event.Module.Name) + + transfer := repository.TransferAttributes{ + TransactionId: transactionId, + Amount: amount, + ChainId: chainId, + FromAcct: fromAcct, + ModuleHash: event.ModuleHash, + ModuleName: moduleName, + RequestKey: requestKey, + ToAcct: toAcct, + HasTokenId: false, + TokenId: nil, + Type: "fungible", + ContractId: nil, + OrderIndex: len(transfers), + } + + transfers = append(transfers, transfer) + } + } + + return transfers +} diff --git a/backfill/process/get_nft_transfers.go b/backfill/process/get_nft_transfers.go new file mode 100644 index 00000000..5cfdcf2a --- /dev/null +++ b/backfill/process/get_nft_transfers.go @@ -0,0 +1,54 @@ +package process + +import ( + "go-backfill/fetch" + "go-backfill/repository" + "log" +) + +func GetNftTransfers(network string, chainId int, events []fetch.Event, reqKey string, transactionId int64) []repository.TransferAttributes { + const TransferNftSignature = "TRANSFER" + const TransferNftParamsLength = 4 + + transfers := make([]repository.TransferAttributes, 0, len(events)) + + for _, event := range events { + if event.Name == TransferNftSignature && len(event.Params) == TransferNftParamsLength { + var tokenId *string + if tokenIdValue, ok := event.Params[0].(string); ok && tokenIdValue != "null" { + tokenId = &tokenIdValue + } + + fromAcct, ok2 := event.Params[1].(string) + toAcct, ok3 := event.Params[2].(string) + amount, ok4 := convertToFloat64(event, 3) + + if !ok2 || !ok3 || !ok4 { + log.Printf("Invalid NFT transfer parameters in event: %+v\n", event) + continue + } + + moduleName := buildModuleName(event.Module.Namespace, event.Module.Name) + + transfer := repository.TransferAttributes{ + TransactionId: transactionId, + Amount: amount, + ChainId: chainId, + FromAcct: fromAcct, + ModuleHash: event.ModuleHash, + ModuleName: moduleName, + RequestKey: reqKey, + ToAcct: toAcct, + HasTokenId: tokenId != nil, + TokenId: tokenId, + Type: "poly-fungible", + ContractId: nil, + OrderIndex: len(transfers), + } + + transfers = append(transfers, transfer) + } + } + + return transfers +} diff --git a/backfill/process/process.go b/backfill/process/process.go new file mode 100644 index 00000000..0fc07c93 --- /dev/null +++ b/backfill/process/process.go @@ -0,0 +1,63 @@ +package process + +import ( + "go-backfill/config" + "go-backfill/fetch" + "log" + "time" + + "github.com/jackc/pgx/v5/pgxpool" +) + +func StartBackfill(LastHeight int, Hash string, ChainId int, SyncMinHeight int, db *pgxpool.Pool) { + env := config.GetConfig() + + network := env.Network + AverageTime := 0.0 + CurrentHeight := LastHeight + + log.Printf("Starting backfill process: %s, %d, %d\n", Hash, LastHeight, SyncMinHeight) + + for CurrentHeight >= SyncMinHeight { + startTime := time.Now() + nextHeight := max(CurrentHeight-env.SyncFetchIntervalInBlocks+1, SyncMinHeight) + log.Printf("Processing height %d to %d...\n", CurrentHeight, nextHeight) + + blocks, err := fetch.FetchPayloadsWithHeaders(network, ChainId, Hash, nextHeight, CurrentHeight) + if err != nil { + log.Fatalf("Error fetching payloads for chain %d, height %d to %d: %v\n", ChainId, nextHeight, CurrentHeight, err) + } + + processedPayloads, err := fetch.ProcessPayloads(blocks) + if err != nil { + log.Fatalf("Error processing payloads for chain %d, height %d to %d: %v\n", ChainId, nextHeight, CurrentHeight, err) + } + + counters, dataSizeTracker, err := savePayloads(network, ChainId, processedPayloads, db) + if err != nil { + log.Fatalf("Error saving payloads for chain %d, height %d to %d -> %v\n", ChainId, nextHeight, CurrentHeight, err) + } + + if env.IsDevelopment { + time.Sleep(5 * time.Second) + } + + // Update the current height + CurrentHeight = nextHeight - 1 + + // Calculate duration and progress + duration := time.Since(startTime) + progress := 100.0 * float64(LastHeight-CurrentHeight) / float64(LastHeight-SyncMinHeight+1) + + // Log progress and stats + log.Printf("(%.2f%%) Processed chain %d in %fs\n", progress, ChainId, duration.Seconds()) + log.Printf("Counters: %d transactions, %d transfers, %d events, %d signers\n", counters.Transactions, counters.Transfers, counters.Events, counters.Signers) + log.Printf("Transactions: %d KB, Transfers: %d KB, Events: %d KB, Signers: %d KB\n", dataSizeTracker.TransactionsKB, dataSizeTracker.TransfersKB, dataSizeTracker.EventsKB, dataSizeTracker.SignersKB) + + // Update average time + AverageTime = (AverageTime + duration.Seconds()) / 2 + log.Printf("Average time: %f\n", AverageTime) + } + + log.Printf("Backfill process completed for chain %d.\n", ChainId) +} diff --git a/backfill/process/process_blocks.go b/backfill/process/process_blocks.go new file mode 100644 index 00000000..c344b926 --- /dev/null +++ b/backfill/process/process_blocks.go @@ -0,0 +1,39 @@ +package process + +import ( + "go-backfill/fetch" + "go-backfill/repository" +) + +func PrepareBlocks(network string, chainId int, payloads []fetch.ProcessedPayload) []repository.BlockAttributes { + + blocks := make([]repository.BlockAttributes, 0, len(payloads)) + + for _, payload := range payloads { + header := payload.Header + block := repository.BlockAttributes{ + Nonce: header.Nonce, + CreationTime: header.CreationTime, + Parent: header.Parent, + Adjacents: header.Adjacents, + Target: header.Target, + PayloadHash: header.PayloadHash, + ChainId: chainId, + Weight: header.Weight, + Height: header.Height, + ChainwebVersion: header.ChainwebVersion, + EpochStart: header.EpochStart, + FeatureFlags: int64(header.FeatureFlags), + Hash: header.Hash, + MinerData: string(payload.MinerData), + TransactionsHash: payload.TransactionsHash, + OutputsHash: payload.OutputsHash, + Coinbase: string(payload.Coinbase), + TransactionsCount: len(payload.Transactions), + } + + blocks = append(blocks, block) + } + + return blocks +} diff --git a/backfill/process/process_events.go b/backfill/process/process_events.go new file mode 100644 index 00000000..257327bf --- /dev/null +++ b/backfill/process/process_events.go @@ -0,0 +1,49 @@ +package process + +import ( + "encoding/json" + "fmt" + "go-backfill/fetch" + "go-backfill/repository" +) + +func PrepareEvents(network string, payload fetch.ProcessedPayload, transactionsId []int64) ([]repository.EventAttributes, error) { + transactions := payload.Transactions + + if len(transactions) == 0 { + return []repository.EventAttributes{}, nil + } + + const avgEventsPerTransaction = 80 + events := make([]repository.EventAttributes, 0, len(transactions)*avgEventsPerTransaction) + + for txIndex, t := range transactions { + + for eventIndex, event := range t.Events { + + module := buildModuleName(event.Module.Namespace, event.Module.Name) + + qualName := buildModuleName(event.Module.Namespace, event.Module.Name) + + paramsJSON, err := json.Marshal(event.Params) + if err != nil { + return []repository.EventAttributes{}, fmt.Errorf("marshaling params for event %s: %w", event.Name, err) + } + + eventRecord := repository.EventAttributes{ + TransactionId: transactionsId[txIndex], + ChainId: payload.Header.ChainId, + Module: module, + Name: event.Name, + Params: paramsJSON, + QualName: qualName, + RequestKey: t.ReqKey, + OrderIndex: eventIndex, + } + events = append(events, eventRecord) + } + + } + + return events, nil +} diff --git a/backfill/process/process_guards.go b/backfill/process/process_guards.go new file mode 100644 index 00000000..095c6601 --- /dev/null +++ b/backfill/process/process_guards.go @@ -0,0 +1,155 @@ +package process + +import ( + "encoding/json" + "fmt" + "go-backfill/fetch" + "go-backfill/repository" + "regexp" + "strconv" +) + +type LocalCmd struct { + NetworkID string `json:"networkId"` + Payload struct { + Exec *struct { + Data map[string]interface{} `json:"data"` + Code string `json:"code"` + } `json:"exec"` + } `json:"payload"` + Meta struct { + ChainID string `json:"chainId"` + Sender string `json:"sender"` + } `json:"meta"` +} + +type Keyset struct { + Pred string `json:"pred"` + Keys []string `json:"keys"` +} + +func PrepareGuards( + processedPayload fetch.ProcessedPayload, +) ([]repository.GuardAttributes, error) { + + transactions := processedPayload.Transactions + + if len(transactions) == 0 { + return []repository.GuardAttributes{}, nil + } + + const avgGuardsPerTransaction = 4 + guards := make([]repository.GuardAttributes, 0, len(transactions)*avgGuardsPerTransaction) + + for _, transaction := range transactions { + var cmdData LocalCmd + + // First, unmarshal to get the JSON-encoded string + var cmdRaw string + err := json.Unmarshal(transaction.Cmd, &cmdRaw) + if err != nil { + return []repository.GuardAttributes{}, fmt.Errorf("failed to unmarshal Cmd as string: %w", err) + } + + // Preprocess the JSON string to fix empty `data` fields + cmdRaw, err = preprocessCmdRaw(cmdRaw) + if err != nil { + return []repository.GuardAttributes{}, fmt.Errorf("failed to preprocess Cmd: %w", err) + } + + // Second, unmarshal the JSON string into the Cmd struct + err = json.Unmarshal([]byte(cmdRaw), &cmdData) + if err != nil { + return []repository.GuardAttributes{}, fmt.Errorf("failed to unmarshal Cmd to struct: %w", err) + } + + sender := cmdData.Meta.Sender + chainId, err := strconv.Atoi(cmdData.Meta.ChainID) + if err != nil { + return []repository.GuardAttributes{}, fmt.Errorf("error converting ChainId to int: %w", err) + } + var keyset *Keyset + if cmdData.Payload.Exec != nil { + keysetName := getKeysetName(cmdData.Payload.Exec.Code) + if keysetName != "" { + if rawValue, exists := cmdData.Payload.Exec.Data[keysetName]; exists { + rawJSON, err := json.Marshal(rawValue) + if err == nil { + var ks Keyset + if json.Unmarshal(rawJSON, &ks) == nil { + keyset = &ks + } + } + } + } + } + + if keyset == nil { + continue + } + + for _, publicKey := range keyset.Keys { + if len(publicKey) > 64 { + fmt.Printf("Invalid public key length: %s\n", transaction.ReqKey) + continue + } + guards = append(guards, repository.GuardAttributes{ + PublicKey: publicKey, + ChainID: chainId, + Predicate: keyset.Pred, + Account: sender, + }) + } + } + + return guards, nil +} + +func preprocessCmdRaw(cmdRaw string) (string, error) { + var cmdMap map[string]interface{} + if err := json.Unmarshal([]byte(cmdRaw), &cmdMap); err != nil { + return "", fmt.Errorf("failed to unmarshal CmdRaw into map: %w", err) + } + + if payload, ok := cmdMap["payload"].(map[string]interface{}); ok { + if exec, ok := payload["exec"].(map[string]interface{}); ok { + if data, ok := exec["data"]; ok { + switch v := data.(type) { + case string: + // Replace empty string with an empty map + if v == "" { + exec["data"] = map[string]interface{}{} + } + case []interface{}: + // Handle data as an array + if len(v) != 0 { + fmt.Printf("Unexpected format for data: %s\n", cmdRaw) + } + exec["data"] = map[string]interface{}{} + default: + // Leave other types unchanged + } + } + } + } + + // Marshal the fixed map back to JSON string + fixedCmdRaw, err := json.Marshal(cmdMap) + if err != nil { + return "", fmt.Errorf("failed to marshal fixed CmdRaw: %w", err) + } + + return string(fixedCmdRaw), nil +} + +func getKeysetName(code string) string { + re := regexp.MustCompile(`(?i)\(read-keyset\s+(?:'|")([a-zA-Z0-9_-]+)(?:'|")\)`) + + matches := re.FindStringSubmatch(code) + + if len(matches) > 1 { + return matches[1] + } + + return "" +} diff --git a/backfill/process/process_signers.go b/backfill/process/process_signers.go new file mode 100644 index 00000000..869a8fd6 --- /dev/null +++ b/backfill/process/process_signers.go @@ -0,0 +1,68 @@ +package process + +import ( + "encoding/json" + "fmt" + "go-backfill/fetch" + "go-backfill/repository" +) + +type Signer struct { + PubKey string `json:"pubKey"` + Clist json.RawMessage `json:"clist"` + Address *string `json:"address"` + Scheme *string `json:"scheme"` +} + +type Cmd struct { + Signers []Signer `json:"signers"` +} + +func PrepareSigners(network string, payload fetch.ProcessedPayload, transactionsId []int64) []repository.SignerAttributes { + transactions := payload.Transactions + + if len(transactions) == 0 { + return []repository.SignerAttributes{} + } + + const avgSignersPerTransaction = 80 + signers := make([]repository.SignerAttributes, 0, len(transactions)*avgSignersPerTransaction) + + for txIndex, transaction := range transactions { + if txIndex >= len(transactionsId) { + fmt.Printf("Warning: No transactionId for transaction index %d\n", txIndex) + break + } + + transactionId := transactionsId[txIndex] + + var cmd Cmd + + // First, unmarshal to get the JSON-encoded string + var cmdRaw string + err := json.Unmarshal(transaction.Cmd, &cmdRaw) + if err != nil { + fmt.Printf("Failed to unmarshal Cmd as string for transaction index %d: %v\n", txIndex, err) + continue + } + + // Second, unmarshal the JSON string into the Cmd struct + err = json.Unmarshal([]byte(cmdRaw), &cmd) + if err != nil { + fmt.Printf("Failed to unmarshal Cmd for transaction index %d: %v\n", txIndex, err) + continue + } + + for signerIndex, signer := range cmd.Signers { + signers = append(signers, repository.SignerAttributes{ + PubKey: signer.PubKey, + Clist: signer.Clist, + OrderIndex: signerIndex, + Scheme: signer.Scheme, + TransactionId: transactionId, + }) + } + } + + return signers +} diff --git a/backfill/process/process_transactions.go b/backfill/process/process_transactions.go new file mode 100644 index 00000000..3bf6cfd8 --- /dev/null +++ b/backfill/process/process_transactions.go @@ -0,0 +1,158 @@ +package process + +import ( + "encoding/json" + "fmt" + "go-backfill/fetch" + "go-backfill/repository" + "strconv" + "strings" +) + +type CmdData struct { + Meta struct { + TTL TtlString `json:"ttl"` + Sender string `json:"sender"` + GasPrice GasPriceString `json:"gasPrice"` + GasLimit GasLimit `json:"gasLimit"` + ChainId string `json:"chainId"` + CreationTime CreationTimeString `json:"creationTime"` + } `json:"meta"` + Nonce string `json:"nonce"` + Payload struct { + Exec struct { + Code json.RawMessage `json:"code"` + Data json.RawMessage `json:"data"` + } `json:"exec"` + Cont *struct { + Proof *string `json:"proof"` + Step int `json:"step"` + } `json:"cont"` + } `json:"payload"` +} + +func PrepareTransactions(network string, blockId int64, payload fetch.ProcessedPayload) ([]repository.TransactionAttributes, error) { + transactions := payload.Transactions + + if len(transactions) == 0 { + return []repository.TransactionAttributes{}, nil + } + + transactionRecords := make([]repository.TransactionAttributes, 0, len(transactions)) + + var cmdData CmdData + var continuationData struct { + PactID *string `json:"pactId"` + } + var resultData map[string]interface{} + + for _, t := range transactions { + cmdData = CmdData{} + resultData = nil + var rawCmd string + continuationData = struct { + PactID *string `json:"pactId"` + }{} + + if err := json.Unmarshal(t.Cmd, &rawCmd); err != nil { + return nil, fmt.Errorf("unmarshaling Cmd JSON for transaction %s: %w", t.Hash, err) + } + + if err := json.Unmarshal([]byte(rawCmd), &cmdData); err != nil { + return nil, fmt.Errorf("unmarshaling raw command for transaction %s: %w", t.Hash, err) + } + + // if err := json.Unmarshal([]byte(rawCmd), &cmdData); err != nil { + // return nil, fmt.Errorf("unmarshaling raw command for transaction %s: %w", t.Hash, err) + // } + + continuationRaw := json.RawMessage("{}") + if string(t.Continuation) != "null" { + continuationRaw = t.Continuation + } + + codeRaw, err := ensureNotEmpty(cmdData.Payload.Exec.Code) + if err != nil { + return nil, fmt.Errorf("ensuring code is not empty for transaction %s: %w", t.Hash, err) + } + dataRaw, err := ensureNotEmpty(cmdData.Payload.Exec.Data) + if err != nil { + return nil, fmt.Errorf("ensuring data is not empty for transaction %s: %w", t.Hash, err) + } + + if err := json.Unmarshal(continuationRaw, &continuationData); err != nil { + return nil, fmt.Errorf("unmarshaling Continuation for transaction %s: %w", t.Hash, err) + } + + rollback := true + if err := json.Unmarshal(t.Result, &resultData); err == nil { + if status, ok := resultData["status"].(string); ok && status == "success" { + rollback = false + } + } + + chainId, err := strconv.Atoi(cmdData.Meta.ChainId) + if err != nil { + return nil, fmt.Errorf("converting ChainId for transaction %s: %w", t.Hash, err) + } + + txId := strconv.Itoa(t.TxId) + // creationTimeStr := strconv.FormatFloat(cmdData.Meta.CreationTime, 'f', -1, 64) + gas := strconv.Itoa(t.Gas) + + var proof *string + var step = 0 + if cmdData.Payload.Cont != nil { + proof = cmdData.Payload.Cont.Proof + step = cmdData.Payload.Cont.Step + } + + nonce := strings.ReplaceAll(cmdData.Nonce, "\\\"", "") + nonce = strings.ReplaceAll(nonce, "\"", "") + transactionRecord := repository.TransactionAttributes{ + BlockId: blockId, + Code: codeRaw, + Data: dataRaw, + ChainId: chainId, + CreationTime: string(cmdData.Meta.CreationTime), + GasLimit: string(cmdData.Meta.GasLimit), + GasPrice: string(cmdData.Meta.GasPrice), + Hash: t.Hash, + Nonce: nonce, + PactId: continuationData.PactID, + Continuation: continuationRaw, + Gas: gas, + Result: t.Result, + Proof: proof, + Logs: t.Logs, + NumEvents: len(t.Events), + RequestKey: t.ReqKey, + Rollback: rollback, + Sender: cmdData.Meta.Sender, + Sigs: t.Sigs, + Step: step, + TTL: string(cmdData.Meta.TTL), + TxId: txId, + } + transactionRecords = append(transactionRecords, transactionRecord) + } + + return transactionRecords, nil +} + +func ensureNotEmpty(raw json.RawMessage) (json.RawMessage, error) { + if len(raw) == 0 || string(raw) == `""` { + return json.RawMessage(`{}`), nil + } + + var decoded interface{} + if err := json.Unmarshal(raw, &decoded); err != nil { + return nil, fmt.Errorf("invalid JSON: %w", err) + } + + if decoded == nil || decoded == "" { + return json.RawMessage(`{}`), nil + } + + return raw, nil +} diff --git a/backfill/process/process_transfers.go b/backfill/process/process_transfers.go new file mode 100644 index 00000000..0d249011 --- /dev/null +++ b/backfill/process/process_transfers.go @@ -0,0 +1,27 @@ +package process + +import ( + "go-backfill/fetch" + "go-backfill/repository" +) + +func PrepareTransfers(network string, payload fetch.ProcessedPayload, transactionsId []int64) []repository.TransferAttributes { + transactions := payload.Transactions + + if len(transactions) == 0 { + return []repository.TransferAttributes{} + } + + const avgTransfersPerTransaction = 80 + transfers := make([]repository.TransferAttributes, 0, len(transactions)*avgTransfersPerTransaction) + + for index, t := range transactions { + ChainId := payload.Header.ChainId + coinTransfers := GetCoinTransfers(t.Events, ChainId, t.ReqKey, transactionsId[index]) + nftTransfers := GetNftTransfers(network, ChainId, t.Events, t.ReqKey, transactionsId[index]) + transfers = append(transfers, coinTransfers...) + transfers = append(transfers, nftTransfers...) + } + + return transfers +} diff --git a/backfill/process/save_payloads.go b/backfill/process/save_payloads.go new file mode 100644 index 00000000..b4686c8d --- /dev/null +++ b/backfill/process/save_payloads.go @@ -0,0 +1,147 @@ +package process + +import ( + "context" + "fmt" + "go-backfill/fetch" + "go-backfill/repository" + "log" + "time" + + "github.com/jackc/pgx/v5/pgxpool" +) + +type Counters struct { + Transactions int + Events int + Transfers int + Signers int +} + +type DataSizeTracker struct { + TransactionsKB int + EventsKB int + TransfersKB int + SignersKB int +} + +func savePayloads(network string, chainId int, processedPayloads []fetch.ProcessedPayload, pool *pgxpool.Pool) (Counters, DataSizeTracker, error) { + startTime := time.Now() + + counters := Counters{ + Transactions: 0, + Events: 0, + Transfers: 0, + Signers: 0, + } + + dataSizeTracker := DataSizeTracker{ + TransactionsKB: 0, + EventsKB: 0, + TransfersKB: 0, + SignersKB: 0, + } + + conn, err := pool.Acquire(context.Background()) + if err != nil { + return Counters{}, DataSizeTracker{}, fmt.Errorf("acquiring connection: %w", err) + } + defer conn.Release() + + tx, err := conn.Begin(context.Background()) + if err != nil { + return Counters{}, DataSizeTracker{}, fmt.Errorf("starting transaction: %w", err) + } + + defer func() { + if err != nil { + _ = tx.Rollback(context.Background()) // Explicitly ignore rollback error + } + }() + + blocks := PrepareBlocks(network, chainId, processedPayloads) + + blockIds, err := repository.SaveBlocks(tx, blocks) + if err != nil { + return Counters{}, DataSizeTracker{}, fmt.Errorf("saving blocks -> %w", err) + } + + var transactionIdsToSave [][]int64 + for index, processedPayload := range processedPayloads { + txs, err := PrepareTransactions(network, blockIds[index], processedPayload) + if err != nil { + return Counters{}, DataSizeTracker{}, fmt.Errorf("saving transactions -> %w", err) + } + transactionIds, err := repository.SaveTransactions(tx, txs) + if err != nil { + return Counters{}, DataSizeTracker{}, fmt.Errorf("saving transactions -> %w", err) + } + + txsSize := approximateSize(txs) + dataSizeTracker.TransactionsKB += txsSize + transactionIdsToSave = append(transactionIdsToSave, transactionIds) + counters.Transactions += len(transactionIds) + } + + for index, processedPayload := range processedPayloads { + events, err := PrepareEvents(network, processedPayload, transactionIdsToSave[index]) + if err != nil { + return Counters{}, DataSizeTracker{}, fmt.Errorf("preparing events -> %w", err) + } + if err := repository.SaveEventsToDatabase(events, tx); err != nil { + return Counters{}, DataSizeTracker{}, fmt.Errorf("saving events -> %w", err) + } + + size := approximateSize(events) + dataSizeTracker.EventsKB += size + counters.Events += len(events) + } + + for index, processedPayload := range processedPayloads { + transfers := PrepareTransfers(network, processedPayload, transactionIdsToSave[index]) + if err := repository.SaveTransfersToDatabase(transfers, tx); err != nil { + return Counters{}, DataSizeTracker{}, fmt.Errorf("saving transfers: %w", err) + } + + size := approximateSize(transfers) + dataSizeTracker.TransfersKB += size + counters.Transfers += len(transfers) + } + + for index, processedPayload := range processedPayloads { + signers := PrepareSigners(network, processedPayload, transactionIdsToSave[index]) + if err := repository.SaveSignersToDatabase(signers, tx); err != nil { + return Counters{}, DataSizeTracker{}, fmt.Errorf("saving signers: %w", err) + } + + size := approximateSize(signers) + dataSizeTracker.SignersKB += size + counters.Signers += len(signers) + } + + // for _, processedPayload := range processedPayloads { + // guards, err := PrepareGuards(processedPayload) + // if err != nil { + // return Counters{}, DataSizeTracker{}, fmt.Errorf("preparing guards -> %w", err) + // } + // if err := repository.SaveGuardsToDatabase(guards, tx); err != nil { + // return Counters{}, DataSizeTracker{}, fmt.Errorf("saving guards -> %w", err) + // } + + // size := approximateSize(guards) + // dataSizeTracker.GuardsKB += size + // counters.Guards += len(guards) + // } + + if err := tx.Commit(context.Background()); err != nil { + return Counters{}, DataSizeTracker{}, fmt.Errorf("committing transaction: %w", err) + } + + dataSizeTracker.TransactionsKB /= 1024 + dataSizeTracker.EventsKB /= 1024 + dataSizeTracker.TransfersKB /= 1024 + dataSizeTracker.SignersKB /= 1024 + + log.Printf("Saved payloads in %fs\n", time.Since(startTime).Seconds()) + return counters, dataSizeTracker, nil +} diff --git a/backfill/process/utils.go b/backfill/process/utils.go new file mode 100644 index 00000000..afb8c356 --- /dev/null +++ b/backfill/process/utils.go @@ -0,0 +1,163 @@ +package process + +import ( + "encoding/json" + "fmt" + "go-backfill/fetch" + "log" + "strconv" +) + +type CreationTimeString string + +func (c *CreationTimeString) UnmarshalJSON(data []byte) error { + // Try to unmarshal as a float + var floatValue float64 + if err := json.Unmarshal(data, &floatValue); err == nil { + *c = CreationTimeString(strconv.FormatFloat(floatValue, 'f', -1, 64)) // Convert float64 to string + return nil + } + + // Try to unmarshal as a string + var stringValue string + if err := json.Unmarshal(data, &stringValue); err == nil { + *c = CreationTimeString(stringValue) + return nil + } + + return fmt.Errorf("data is neither float64 nor string: %s", string(data)) +} + +type TtlString string + +func (g *TtlString) UnmarshalJSON(data []byte) error { + // Try to unmarshal as a string + var stringValue string + if err := json.Unmarshal(data, &stringValue); err == nil { + *g = TtlString(stringValue) + return nil + } + + // Try to unmarshal as an integer + var intValue int + if err := json.Unmarshal(data, &intValue); err == nil { + *g = TtlString(fmt.Sprintf("%d", intValue)) // Convert int to string + return nil + } + + // Try to unmarshal as a float + var floatValue float64 + if err := json.Unmarshal(data, &floatValue); err == nil { + *g = TtlString(fmt.Sprintf("%g", floatValue)) // Convert float to string + return nil + } + + // If all attempts fail, return an error + return fmt.Errorf("data is neither string, int, nor float64: %s", string(data)) +} + +type GasPriceString string + +func (g *GasPriceString) UnmarshalJSON(data []byte) error { + // Try to unmarshal as a float + var floatValue float64 + if err := json.Unmarshal(data, &floatValue); err == nil { + *g = GasPriceString(fmt.Sprintf("%g", floatValue)) // Convert float64 to string + return nil + } + + // Try to unmarshal as a string + var stringValue string + if err := json.Unmarshal(data, &stringValue); err == nil { + *g = GasPriceString(stringValue) + return nil + } + + return fmt.Errorf("data is neither float64 nor string: %s", string(data)) +} + +type GasLimit string + +func (g *GasLimit) UnmarshalJSON(data []byte) error { + // Attempt to unmarshal the data as an int + var intValue int + if err := json.Unmarshal(data, &intValue); err == nil { + *g = GasLimit(strconv.Itoa(intValue)) // Convert int to string and assign + return nil + } + + // Attempt to unmarshal the data as a string + var stringValue string + if err := json.Unmarshal(data, &stringValue); err == nil { + *g = GasLimit(stringValue) // Assign the string value directly + return nil + } + + // If neither, return an error + return fmt.Errorf("data is neither int nor string: %s", string(data)) +} + +func convertToFloat64(event fetch.Event, index int) (float64, bool) { + // Handle if it's a map + if amountMap, ok := event.Params[index].(map[string]interface{}); ok { + // Extract the value from the map + if decimalValue, exists := amountMap["decimal"]; exists { + switch v := decimalValue.(type) { + case float64: + return v, true + case string: + parsedValue, err := strconv.ParseFloat(v, 64) + if err != nil { + log.Printf("Error: unable to parse decimal string to float64: %v\n", err) + return 0, false // Return default value and false on error + } + return parsedValue, true + case int: + return float64(v), true + default: + log.Printf("Error: unexpected type for decimal value: %T\n", v) + return 0, false + } + } else { + log.Printf("Error: decimal key not found in map: %v\n", amountMap) + return 0, false + } + } + + // Handle if it's a float64 + if amountFloat, ok := event.Params[index].(float64); ok { + return amountFloat, true + } + + // Handle if it's an int + if amountInt, ok := event.Params[index].(int); ok { + return float64(amountInt), true + } + + // If it doesn't match any expected type + log.Printf("Error: event.Params[%d] is neither a map, float64, nor int: %v\n", index, event.Params[index]) + return 0, false +} + +func buildModuleName(namespace, name string) string { + if namespace != "" { + return namespace + "." + name + } + return name +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func approximateSize(v interface{}) int { + data, err := json.Marshal(v) + if err != nil { + log.Printf("Error estimating size: %v\n", err) + return 0 + } + return len(data) // Return size in bytes +} diff --git a/backfill/recreate-indexes/recreate-indexes.go b/backfill/recreate-indexes/recreate-indexes.go new file mode 100644 index 00000000..72b40831 --- /dev/null +++ b/backfill/recreate-indexes/recreate-indexes.go @@ -0,0 +1,95 @@ +package main + +import ( + "database/sql" + "flag" + "fmt" + "go-backfill/config" + "log" + "time" + + _ "github.com/lib/pq" // PostgreSQL driver +) + +func main() { + envFile := flag.String("env", ".env", "Path to the .env file") + flag.Parse() + config.InitEnv(*envFile) + env := config.GetConfig() + connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", + env.DbHost, env.DbPort, env.DbUser, env.DbPassword, env.DbName) + + db, err := sql.Open("postgres", connStr) + if err != nil { + log.Fatalf("Failed to connect to database: %v", err) + } + defer db.Close() + + log.Println("Connected to database") + + // SQL commands grouped by table + commands := []struct { + description string + query string + }{ + // // CREATE INDEX commands for Balances + // {"Create unique index on Balances", `CREATE UNIQUE INDEX "balances_unique_constraint" ON "Balances" (network, "chainId", account, qualname, "tokenId");`}, + // {"Create account index on Balances", `CREATE INDEX "balances_account_index" ON "Balances" (account);`}, + // {"Create tokenId index on Balances", `CREATE INDEX "balances_tokenid_index" ON "Balances" ("tokenId");`}, + // {"Create contractId index on Balances", `CREATE INDEX "balances_contractid_index" ON "Balances" ("contractId");`}, + // {"Create search index on Balances", `CREATE INDEX "balances_search_idx" ON "Balances" (LOWER(account));`}, + + // // CREATE INDEX commands for Blocks + // {"Create height index on Blocks", `CREATE INDEX "blocks_height_idx" ON "Blocks" (height);`}, + // {"Create chainId height index on Blocks", `CREATE INDEX "blocks_chainid_height_idx" ON "Blocks" ("chainId", height);`}, + // {"Create chainId index on Blocks", `CREATE INDEX "blocks_chainid_idx" ON "Blocks" ("chainId");`}, + // {"Create canonical index on Blocks", `CREATE INDEX "blocks_canonical_idx" ON "Blocks" (canonical);`}, + // {"Create GIN trgm parent index on Blocks", `CREATE INDEX "blocks_trgm_parent_idx" ON "Blocks" USING gin (LOWER(parent) gin_trgm_ops);`}, + + // // CREATE INDEX commands for Contracts + // {"Create unique index on Contracts", `CREATE UNIQUE INDEX "contract_unique_constraint" ON "Contracts" (network, "chainId", module, "tokenId");`}, + // {"Create search index on Contracts", `CREATE INDEX "contracts_search_idx" ON "Contracts" (LOWER(module));`}, + + // // CREATE INDEX commands for Events + // {"Create transactionId index on Events", `CREATE INDEX "events_transactionid_idx" ON "Events" ("transactionId");`}, + + // CREATE INDEX commands for Signers + {"Create publicKey transactionId index on Signers", `CREATE INDEX "signers_pubkey_transactionid_idx" ON "Signers" (pubkey, "transactionId");`}, + + // CREATE INDEX commands for Transactions + {"Create requestkey index on Transactions", `CREATE INDEX "transactions_requestkey_idx" ON "Transactions" (requestkey);`}, + {"Create sender index on Transactions", `CREATE INDEX "transactions_sender_idx" ON "Transactions" (sender);`}, + {"Create chainId blockId index on Transactions", `CREATE INDEX "transactions_chainid_blockid_idx" ON "Transactions" ("chainId", "blockId");`}, + {"Create hash index on Transactions", `CREATE INDEX "transactions_hash_idx" ON "Transactions" (hash);`}, + {"Create canonical index on Transactions", `CREATE INDEX "transactions_canonical_idx" ON "Transactions" (canonical);`}, + {"Create GIN trgm requestkey index on Transactions", `CREATE INDEX "transactions_trgm_requestkey_idx" ON "Transactions" USING gin (LOWER(requestkey) gin_trgm_ops);`}, + {"Create GIN trgm hash index on Transactions", `CREATE INDEX "transactions_trgm_hash_idx" ON "Transactions" USING gin (LOWER(hash) gin_trgm_ops);`}, + {"Create GIN trgm txid index on Transactions", `CREATE INDEX "transactions_trgm_txid_idx" ON "Transactions" USING gin (LOWER(txid) gin_trgm_ops);`}, + {"Create GIN trgm pactid index on Transactions", `CREATE INDEX "transactions_trgm_pactid_idx" ON "Transactions" USING gin (LOWER(pactid) gin_trgm_ops);`}, + {"Create GIN trgm sender index on Transactions", `CREATE INDEX "transactions_trgm_sender_idx" ON "Transactions" USING gin (LOWER(sender) gin_trgm_ops);`}, + + // CREATE INDEX commands for Transfers + {"Create contractId index on Transfers", `CREATE INDEX "transfers_contractid_idx" ON "Transfers" ("contractId");`}, + {"Create type index on Transfers", `CREATE INDEX "transfers_type_idx" ON "Transfers" (type);`}, + {"Create transactionId index on Transfers", `CREATE INDEX "transfers_transactionid_idx" ON "Transfers" ("transactionId");`}, + {"Create modulename index on Transfers", `CREATE INDEX "transfers_modulename_idx" ON "Transfers" (modulename);`}, + {"Create from_acct modulename index on Transfers", `CREATE INDEX "transfers_from_acct_modulename_idx" ON "Transfers" (from_acct, modulename);`}, + } + + for i, cmd := range commands { + log.Printf("Executing (%d/%d): %s", i+1, len(commands), cmd.description) + + start := time.Now() + _, err := db.Exec(cmd.query) + elapsed := time.Since(start) + + if err != nil { + log.Printf("Error executing (%d): %s - %v", i+1, cmd.description, err) + break // Optionally exit on error or log and continue + } else { + log.Printf("Successfully executed (%d): %s in %v", i+1, cmd.description, elapsed) + } + } + + log.Println("All commands executed") +} diff --git a/backfill/repository/block_repository.go b/backfill/repository/block_repository.go new file mode 100644 index 00000000..5f3b8814 --- /dev/null +++ b/backfill/repository/block_repository.go @@ -0,0 +1,99 @@ +package repository + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/jackc/pgx/v5" +) + +type BlockAttributes struct { + Nonce string + CreationTime int64 + Parent string + Adjacents map[string]string + Target string + PayloadHash string + ChainId int + Weight string + Height int + ChainwebVersion string + EpochStart int64 + FeatureFlags int64 + Hash string + MinerData string + TransactionsHash string + OutputsHash string + Coinbase string + TransactionsCount int +} + +func SaveBlocks(tx pgx.Tx, blocks []BlockAttributes) ([]int64, error) { + if len(blocks) == 0 { + return nil, nil + } + + query := ` + INSERT INTO "Blocks" ( + nonce, "creationTime", parent, adjacents, target, "payloadHash", + "chainId", weight, height, "chainwebVersion", "epochStart", + "featureFlags", hash, "minerData", "transactionsHash", + "outputsHash", coinbase, "transactionsCount", "createdAt", "updatedAt" + ) + VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20) + RETURNING id + ` + + now := time.Now() + + batch := &pgx.Batch{} + for _, block := range blocks { + adjacentsJSON, err := json.Marshal(block.Adjacents) + if err != nil { + return nil, fmt.Errorf("failed to marshal adjacents for block %s: %v", block.Hash, err) + } + + batch.Queue( + query, + block.Nonce, + block.CreationTime, + block.Parent, + adjacentsJSON, + block.Target, + block.PayloadHash, + block.ChainId, + block.Weight, + block.Height, + block.ChainwebVersion, + block.EpochStart, + block.FeatureFlags, + block.Hash, + block.MinerData, + block.TransactionsHash, + block.OutputsHash, + block.Coinbase, + block.TransactionsCount, + now, + now, + ) + } + + br := tx.SendBatch(context.Background(), batch) + defer br.Close() + + blockIds := make([]int64, 0, len(blocks)) + + // Collect IDs for each queued query + for i := 0; i < len(blocks); i++ { + var id int64 + if err := br.QueryRow().Scan(&id); err != nil { + return nil, fmt.Errorf("failed to execute batch for block %d: %v", i, err) + } + blockIds = append(blockIds, id) + } + + return blockIds, nil +} diff --git a/backfill/repository/event_repository.go b/backfill/repository/event_repository.go new file mode 100644 index 00000000..afd1d0dc --- /dev/null +++ b/backfill/repository/event_repository.go @@ -0,0 +1,65 @@ +package repository + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/jackc/pgx/v5" +) + +type EventAttributes struct { + TransactionId int64 `json:"transactionId"` + ChainId int `json:"chainId"` + Module string `json:"module"` + Name string `json:"name"` + Params json.RawMessage `json:"params"` + QualName string `json:"qualName"` + RequestKey string `json:"requestKey"` + OrderIndex int `json:"orderIndex"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +func SaveEventsToDatabase(events []EventAttributes, db pgx.Tx) error { + if len(events) == 0 { + return nil + } + + query := ` + INSERT INTO "Events" + ("transactionId", "chainId", "module", name, params, qualname, requestkey, "orderIndex", "createdAt", "updatedAt") + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) + ` + + now := time.Now() + + batch := &pgx.Batch{} + for _, event := range events { + batch.Queue( + query, + event.TransactionId, + event.ChainId, + event.Module, + event.Name, + event.Params, + event.QualName, + event.RequestKey, + event.OrderIndex, + now, + now, + ) + } + + br := db.SendBatch(context.Background(), batch) + defer br.Close() + + for i := 0; i < len(events); i++ { + if _, err := br.Exec(); err != nil { + return fmt.Errorf("failed to execute batch for event %d: %v", i, err) + } + } + + return nil +} diff --git a/backfill/repository/guard_repository.go b/backfill/repository/guard_repository.go new file mode 100644 index 00000000..65300fc2 --- /dev/null +++ b/backfill/repository/guard_repository.go @@ -0,0 +1,58 @@ +package repository + +import ( + "context" + "fmt" + "time" + + "github.com/jackc/pgx/v5" +) + +type GuardAttributes struct { + PublicKey string `json:"publicKey"` + ChainID int `json:"chainId"` + Account string `json:"account"` + Predicate string `json:"predicate"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +func SaveGuardsToDatabase(guards []GuardAttributes, db pgx.Tx) error { + if len(guards) == 0 { + return nil + } + + query := ` + INSERT INTO "Guards" + ("publicKey", "chainId", "account", "predicate", "createdAt", "updatedAt") + VALUES ($1, $2, $3, $4, $5, $6) + ON CONFLICT ("publicKey", "chainId", "account") DO NOTHING + ` + + now := time.Now() + batch := &pgx.Batch{} + + for _, guard := range guards { + batch.Queue( + query, + guard.PublicKey, + guard.ChainID, + guard.Account, + guard.Predicate, + now, + now, + ) + } + + br := db.SendBatch(context.Background(), batch) + defer br.Close() + + // Check for errors in each batch execution + for i := 0; i < len(guards); i++ { + if _, err := br.Exec(); err != nil { + return fmt.Errorf("failed to execute batch for guard index %d: %v", i, err) + } + } + + return nil +} diff --git a/backfill/repository/signer_repository.go b/backfill/repository/signer_repository.go new file mode 100644 index 00000000..f854b174 --- /dev/null +++ b/backfill/repository/signer_repository.go @@ -0,0 +1,61 @@ +package repository + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/jackc/pgx/v5" +) + +type SignerAttributes struct { + TransactionId int64 `json:"transactionId"` + Address *string `json:"address"` + Clist json.RawMessage `json:"clist"` + PubKey string `json:"pubKey"` + OrderIndex int `json:"orderIndex"` + Scheme *string `json:"scheme"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +func SaveSignersToDatabase(signers []SignerAttributes, db pgx.Tx) error { + if len(signers) == 0 { + return nil + } + + query := ` + INSERT INTO "Signers" + ("transactionId", address, "orderIndex", pubkey, clist, scheme, "createdAt", "updatedAt") + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + ` + + now := time.Now() + batch := &pgx.Batch{} + for _, signer := range signers { + batch.Queue( + query, + signer.TransactionId, + signer.Address, + signer.OrderIndex, + signer.PubKey, + signer.Clist, + signer.Scheme, + now, + now, + ) + } + + br := db.SendBatch(context.Background(), batch) + defer br.Close() + + // Check for errors in each batch execution + for i := 0; i < len(signers); i++ { + if _, err := br.Exec(); err != nil { + return fmt.Errorf("failed to execute batch for signer index %d: %v", i, err) + } + } + + return nil +} diff --git a/backfill/repository/transaction_repository.go b/backfill/repository/transaction_repository.go new file mode 100644 index 00000000..0ef606fa --- /dev/null +++ b/backfill/repository/transaction_repository.go @@ -0,0 +1,127 @@ +package repository + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/jackc/pgx/v5" +) + +type TransactionAttributes struct { + BlockId int64 `json:"blockId"` + Code json.RawMessage `json:"code"` // JSONB column + Data json.RawMessage `json:"data"` // JSONB column + ChainId int `json:"chainId"` + CreationTime string `json:"creationTime"` + GasLimit string `json:"gasLimit"` + GasPrice string `json:"gasPrice"` + Hash string `json:"hash"` + Nonce string `json:"nonce"` + PactId *string `json:"pactId"` + Continuation json.RawMessage `json:"continuation"` // JSONB column + Gas string `json:"gas"` + Result json.RawMessage `json:"result"` // JSONB column + Logs string `json:"logs"` + Proof *string `json:"proof"` + NumEvents int `json:"numEvents"` + RequestKey string `json:"requestKey"` + Rollback bool `json:"rollback"` + Sender string `json:"sender"` + Sigs json.RawMessage `json:"sigs"` // JSONB column + Step int `json:"step"` + TTL string `json:"ttl"` + TxId string `json:"txId"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +func SaveTransactions(db pgx.Tx, transactions []TransactionAttributes) ([]int64, error) { + if len(transactions) == 0 { + return nil, nil + } + + query := ` + INSERT INTO "Transactions" + ("blockId", code, data, "chainId", creationtime, gaslimit, gasprice, hash, nonce, pactid, continuation, gas, result, logs, proof, num_events, requestkey, rollback, sender, sigs, step, ttl, txid, "createdAt", "updatedAt") + VALUES + ($1, $2::jsonb, $3::jsonb, $4, $5, $6, $7, $8, $9, $10, $11::jsonb, $12, $13::jsonb, $14, $15, $16, $17, $18, $19, $20::jsonb, $21, $22, $23, $24, $25) + RETURNING id + ` + + now := time.Now() + batch := &pgx.Batch{} + + for _, t := range transactions { + code, err := json.Marshal(t.Code) + if err != nil { + return nil, fmt.Errorf("failed to marshal code: %v", err) + } + + data, err := json.Marshal(t.Data) + if err != nil { + return nil, fmt.Errorf("failed to marshal data: %v", err) + } + + continuation, err := json.Marshal(t.Continuation) + if err != nil { + return nil, fmt.Errorf("failed to marshal continuation: %v", err) + } + + result, err := json.Marshal(t.Result) + if err != nil { + return nil, fmt.Errorf("failed to marshal result: %v", err) + } + + sigs, err := json.Marshal(t.Sigs) + if err != nil { + return nil, fmt.Errorf("failed to marshal sigs: %v", err) + } + + batch.Queue( + query, + t.BlockId, + code, + data, + t.ChainId, + t.CreationTime, + t.GasLimit, + t.GasPrice, + t.Hash, + t.Nonce, + t.PactId, + continuation, + t.Gas, + result, + t.Logs, + t.Proof, + t.NumEvents, + t.RequestKey, + t.Rollback, + t.Sender, + sigs, + t.Step, + t.TTL, + t.TxId, + now, + now, + ) + } + + br := db.SendBatch(context.Background(), batch) + defer br.Close() + + transactionIds := make([]int64, 0, len(transactions)) + + // Collect IDs for each queued query + for i := 0; i < len(transactions); i++ { + var id int64 + if err := br.QueryRow().Scan(&id); err != nil { + return nil, fmt.Errorf("failed to execute batch for transaction %d: %v", i, err) + } + transactionIds = append(transactionIds, id) + } + + return transactionIds, nil +} diff --git a/backfill/repository/transfer_repository.go b/backfill/repository/transfer_repository.go new file mode 100644 index 00000000..41ae9c0a --- /dev/null +++ b/backfill/repository/transfer_repository.go @@ -0,0 +1,75 @@ +package repository + +import ( + "context" + "fmt" + "time" + + "github.com/jackc/pgx/v5" +) + +type TransferAttributes struct { + TransactionId int64 `json:"transactionId"` + Amount float64 `json:"amount"` + ChainId int `json:"chainId"` + FromAcct string `json:"from_acct"` + ModuleHash string `json:"modulehash"` + ModuleName string `json:"modulename"` + RequestKey string `json:"requestkey"` + ToAcct string `json:"to_acct"` + HasTokenId bool `json:"hasTokenId"` + TokenId *string `json:"tokenId"` + Type string `json:"type"` + ContractId *string `json:"contractId"` + OrderIndex int `json:"orderIndex"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +func SaveTransfersToDatabase(transfers []TransferAttributes, db pgx.Tx) error { + if len(transfers) == 0 { + return nil + } + + query := ` + INSERT INTO "Transfers" + ("transactionId", amount, "chainId", from_acct, modulehash, modulename, requestkey, to_acct, "hasTokenId", "tokenId", "type", "contractId", "orderIndex", "createdAt", "updatedAt") + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) + ` + + now := time.Now() + + batch := &pgx.Batch{} + for _, transfer := range transfers { + batch.Queue( + query, + transfer.TransactionId, + transfer.Amount, + transfer.ChainId, + transfer.FromAcct, + transfer.ModuleHash, + transfer.ModuleName, + transfer.RequestKey, + transfer.ToAcct, + transfer.HasTokenId, + transfer.TokenId, + transfer.Type, + transfer.ContractId, + transfer.OrderIndex, + now, + now, + ) + } + + br := db.SendBatch(context.Background(), batch) + defer br.Close() + + // Check for errors in each batch execution + for i := 0; i < len(transfers); i++ { + if _, err := br.Exec(); err != nil { + return fmt.Errorf("failed to execute batch for transfer index %d: %v", i, err) + } + } + + return nil +} diff --git a/backfill/sqls/before-backfill.sql b/backfill/sqls/before-backfill.sql new file mode 100644 index 00000000..61a5ad88 --- /dev/null +++ b/backfill/sqls/before-backfill.sql @@ -0,0 +1,73 @@ +-- Drop indexes from the Balances table +DROP INDEX IF EXISTS "balances_unique_constraint"; +DROP INDEX IF EXISTS "balances_account_index"; +DROP INDEX IF EXISTS "balances_tokenid_index"; +DROP INDEX IF EXISTS "balances_contractid_index"; +DROP INDEX IF EXISTS "balances_search_idx"; + +-- Drop indexes from the Blocks table +DROP INDEX IF EXISTS "blocks_chainwebVersion_chainid_hash_unique_idx"; +DROP INDEX IF EXISTS "blocks_height_idx"; +DROP INDEX IF EXISTS "blocks_chainid_height_idx"; +DROP INDEX IF EXISTS "blocks_chainid_idx"; +DROP INDEX IF EXISTS "blocks_canonical_idx"; +DROP INDEX IF EXISTS "blocks_trgm_parent_idx"; + +-- Drop indexes from the Contracts table +DROP INDEX IF EXISTS "contract_unique_constraint"; +DROP INDEX IF EXISTS "contracts_search_idx"; + +-- Drop indexes from the Events table +DROP INDEX IF EXISTS "events_transactionid_idx"; + +-- Drop indexes from the Guards table +DROP INDEX IF EXISTS "signers_pubkey_transactionid_idx"; + +-- Drop indexes from the Signers table +DROP INDEX IF EXISTS "signers_pubkey_transactionid_idx"; + +-- Drop indexes from the Transactions table +DROP INDEX IF EXISTS "transactions_requestkey_idx"; +DROP INDEX IF EXISTS "transactions_blockId_idx"; +DROP INDEX IF EXISTS "transactions_sender_idx"; +DROP INDEX IF EXISTS "transactions_chainId_idx"; +DROP INDEX IF EXISTS "transactions_chainid_blockid_idx"; +DROP INDEX IF EXISTS "transactions_hash_idx"; +DROP INDEX IF EXISTS "transactions_canonical_idx"; +DROP INDEX IF EXISTS "transactions_trgm_requestkey_idx"; +DROP INDEX IF EXISTS "transactions_trgm_hash_idx"; +DROP INDEX IF EXISTS "transactions_trgm_txid_idx"; +DROP INDEX IF EXISTS "transactions_trgm_pactid_idx"; +DROP INDEX IF EXISTS "transactions_trgm_sender_idx"; + +-- Drop indexes from the Transfers table +DROP INDEX IF EXISTS "transfers_type_idx"; +DROP INDEX IF EXISTS "transfers_transactionid_idx"; +DROP INDEX IF EXISTS "transfers_hasTokenId_idx"; +DROP INDEX IF EXISTS "transfers_contractid_idx"; +DROP INDEX IF EXISTS "transfers_modulename_idx"; +DROP INDEX IF EXISTS "transfers_from_acct_modulename_idx"; + +-- Drop constraints from the Balances table +ALTER TABLE "Balances" DROP CONSTRAINT IF EXISTS "Balances_pkey"; + +-- Drop constraints from the Blocks table +ALTER TABLE "Blocks" DROP CONSTRAINT IF EXISTS "Blocks_pkey"; + +-- Drop constraints from the Contracts table +ALTER TABLE "Contracts" DROP CONSTRAINT IF EXISTS "Contracts_pkey"; + +-- Drop constraints from the Events table +ALTER TABLE "Events" DROP CONSTRAINT IF EXISTS "Events_pkey"; + +-- Drop constraints from the Guards table +ALTER TABLE "Guards" DROP CONSTRAINT IF EXISTS "Guards_pkey"; + +-- Drop constraints from the Signers table +ALTER TABLE "Signers" DROP CONSTRAINT IF EXISTS "Signers_pkey"; + +-- Drop constraints from the Transactions table +ALTER TABLE "Transactions" DROP CONSTRAINT IF EXISTS "Transactions_pkey"; + +-- Drop constraints from the Transfers table +ALTER TABLE "Transfers" DROP CONSTRAINT IF EXISTS "Transfers_pkey"; diff --git a/csvs/miner_rewards.csv b/csvs/miner_rewards.csv new file mode 100644 index 00000000..42723a8c --- /dev/null +++ b/csvs/miner_rewards.csv @@ -0,0 +1,1437 @@ +87600,23.04523 +175200,22.97878 +262800,22.91249 +350400,22.84644 +438000,22.78052 +525600,22.71485 +613200,22.64932 +700800,22.58402 +788400,22.51887 +876000,22.45395 +963600,22.38917 +1051200,22.32462 +1138800,22.26022 +1226400,22.19604 +1314000,22.13201 +1401600,22.0682 +1489200,22.00454 +1576800,21.9411 +1664400,21.8778 +1752000,21.81473 +1839600,21.75179 +1927200,21.68909 +2014800,21.62651 +2102400,21.56417 +2190000,21.50195 +2277600,21.43997 +2365200,21.3781 +2452800,21.31649 +2540400,21.25497 +2628000,21.19372 +2715600,21.13255 +2803200,21.07165 +2890800,21.01083 +2978400,20.95029 +3066000,20.88982 +3153600,20.82962 +3241200,20.76951 +3328800,20.70965 +3416400,20.64988 +3504000,20.59037 +3591600,20.53095 +3679200,20.47178 +3766800,20.4127 +3854400,20.35387 +3942000,20.29513 +4029600,20.23664 +4117200,20.17824 +4204800,20.12008 +4292400,20.06203 +4380000,20.00419 +4467600,19.94648 +4555200,19.88898 +4642800,19.8316 +4730400,19.77442 +4818000,19.71738 +4905600,19.66053 +4993200,19.60382 +5080800,19.54729 +5168400,19.49091 +5256000,19.43471 +5343600,19.37865 +5431200,19.32277 +5518800,19.26704 +5606400,19.21148 +5694000,19.15607 +5781600,19.10083 +5869200,19.04574 +5956800,18.99081 +6044400,18.93605 +6132000,18.88143 +6219600,18.82699 +6307200,18.77268 +6394800,18.71855 +6482400,18.66456 +6570000,18.61074 +6657600,18.55706 +6745200,18.50355 +6832800,18.45018 +6920400,18.39697 +7008000,18.34392 +7095600,18.29101 +7183200,18.23827 +7270800,18.18566 +7358400,18.13322 +7446000,18.08092 +7533600,18.02878 +7621200,17.97678 +7708800,17.92495 +7796400,17.87324 +7884000,17.82171 +7971600,17.77029 +8059200,17.71907 +8146800,17.66794 +8234400,17.61701 +8322000,17.56619 +8409600,17.51554 +8497200,17.46501 +8584800,17.41466 +8672400,17.36442 +8760000,17.31436 +8847600,17.26441 +8935200,17.21464 +9022800,17.16497 +9110400,17.11549 +9198000,17.06611 +9285600,17.01691 +9373200,16.96782 +9460800,16.9189 +9548400,16.87009 +9636000,16.82145 +9723600,16.77293 +9811200,16.72456 +9898800,16.67633 +9986400,16.62823 +10074000,16.58028 +10161600,16.53246 +10249200,16.48479 +10336800,16.43724 +10424400,16.38984 +10512000,16.34257 +10599600,16.29544 +10687200,16.24844 +10774800,16.20159 +10862400,16.15486 +10950000,16.10827 +11037600,16.06182 +11125200,16.01549 +11212800,15.96931 +11300400,15.92325 +11388000,15.87733 +11475600,15.83154 +11563200,15.78588 +11650800,15.74036 +11738400,15.69496 +11826000,15.6497 +11913600,15.60457 +12001200,15.55956 +12088800,15.5147 +12176400,15.46994 +12264000,15.42534 +12351600,15.38084 +12439200,15.3365 +12526800,15.29225 +12614400,15.24817 +12702000,15.20417 +12789600,15.16035 +12877200,15.1166 +12964800,15.07303 +13052400,15.02954 +13140000,14.98621 +13227600,14.94298 +13315200,14.89989 +13402800,14.85692 +13490400,14.81407 +13578000,14.77135 +13665600,14.72875 +13753200,14.68627 +13840800,14.64392 +13928400,14.60168 +14016000,14.55958 +14103600,14.51758 +14191200,14.47572 +14278800,14.43397 +14366400,14.39235 +14454000,14.35083 +14541600,14.30946 +14629200,14.26817 +14716800,14.22705 +14804400,14.18599 +14892000,14.14511 +14979600,14.10428 +15067200,14.06364 +15154800,14.02305 +15242400,13.98264 +15330000,13.94228 +15417600,13.90211 +15505200,13.86197 +15592800,13.82204 +15680400,13.78213 +15768000,13.74244 +15855600,13.70275 +15943200,13.66329 +16030800,13.62382 +16118400,13.5846 +16206000,13.54535 +16293600,13.50636 +16381200,13.46733 +16468800,13.42857 +16556400,13.38977 +16644000,13.35122 +16731600,13.31265 +16819200,13.27433 +16906800,13.23597 +16994400,13.19788 +17082000,13.15973 +17169600,13.12187 +17257200,13.08394 +17344800,13.04629 +17432400,13.00858 +17520000,12.97115 +17607600,12.93366 +17695200,12.89644 +17782800,12.85916 +17870400,12.82217 +17958000,12.78509 +18045600,12.74832 +18133200,12.71146 +18220800,12.67489 +18308400,12.63825 +18396000,12.60189 +18483600,12.56546 +18571200,12.5293 +18658800,12.49309 +18746400,12.45714 +18834000,12.42113 +18921600,12.3854 +19009200,12.34959 +19096800,12.31406 +19184400,12.27846 +19272000,12.24314 +19359600,12.20774 +19447200,12.17263 +19534800,12.13742 +19622400,12.10252 +19710000,12.06752 +19797600,12.03281 +19885200,11.99802 +19972800,11.96351 +20060400,11.92891 +20148000,11.89461 +20235600,11.8602 +20323200,11.8261 +20410800,11.79189 +20498400,11.75799 +20586000,11.72397 +20673600,11.69027 +20761200,11.65645 +20848800,11.62294 +20936400,11.58931 +21024000,11.556 +21111600,11.52256 +21199200,11.48944 +21286800,11.45619 +21374400,11.42327 +21462000,11.39021 +21549600,11.35747 +21637200,11.32461 +21724800,11.29206 +21812400,11.25938 +21900000,11.22703 +21987600,11.19453 +22075200,11.16236 +22162800,11.13006 +22250400,11.09807 +22338000,11.06595 +22425600,11.03415 +22513200,11.00222 +22600800,10.9706 +22688400,10.93885 +22776000,10.90741 +22863600,10.87585 +22951200,10.84459 +23038800,10.81321 +23126400,10.78213 +23214000,10.75093 +23301600,10.72003 +23389200,10.689 +23476800,10.65829 +23564400,10.62744 +23652000,10.5969 +23739600,10.56623 +23827200,10.53587 +23914800,10.50537 +24002400,10.47519 +24090000,10.44486 +24177600,10.41486 +24265200,10.3847 +24352800,10.35488 +24440400,10.32488 +24528000,10.29524 +24615600,10.26542 +24703200,10.23594 +24790800,10.2063 +24878400,10.17698 +24966000,10.14752 +25053600,10.11836 +25141200,10.08908 +25228800,10.06008 +25316400,10.03097 +25404000,10.00214 +25491600,9.97319 +25579200,9.94454 +25666800,9.91575 +25754400,9.88726 +25842000,9.85864 +25929600,9.83031 +26017200,9.80186 +26104800,9.77369 +26192400,9.74541 +26280000,9.7174 +26367600,9.68928 +26455200,9.66143 +26542800,9.63347 +26630400,9.60579 +26718000,9.57798 +26805600,9.55047 +26893200,9.52281 +26980800,9.49546 +27068400,9.46797 +27156000,9.44077 +27243600,9.41344 +27331200,9.38639 +27418800,9.35922 +27506400,9.33233 +27594000,9.30532 +27681600,9.27858 +27769200,9.25172 +27856800,9.22514 +27944400,9.19843 +28032000,9.17201 +28119600,9.14545 +28207200,9.11919 +28294800,9.09277 +28382400,9.06667 +28470000,9.0404 +28557600,9.01445 +28645200,8.98833 +28732800,8.96253 +28820400,8.93656 +28908000,8.91091 +28995600,8.88509 +29083200,8.85959 +29170800,8.83391 +29258400,8.80856 +29346000,8.78304 +29433600,8.75782 +29521200,8.73245 +29608800,8.70738 +29696400,8.68216 +29784000,8.65723 +29871600,8.63215 +29959200,8.60737 +30046800,8.58243 +30134400,8.5578 +30222000,8.533 +30309600,8.50851 +30397200,8.48385 +30484800,8.45951 +30572400,8.43498 +30660000,8.41079 +30747600,8.3864 +30835200,8.36234 +30922800,8.3381 +31010400,8.31418 +31098000,8.29007 +31185600,8.2663 +31273200,8.24232 +31360800,8.21869 +31448400,8.19485 +31536000,8.17135 +31623600,8.14765 +31711200,8.12429 +31798800,8.10072 +31886400,8.0775 +31974000,8.05406 +32061600,8.03098 +32149200,8.00767 +32236800,7.98473 +32324400,7.96155 +32412000,7.93874 +32499600,7.91569 +32587200,7.89302 +32674800,7.8701 +32762400,7.84756 +32850000,7.82477 +32937600,7.80236 +33025200,7.7797 +33112800,7.75743 +33200400,7.73489 +33288000,7.71275 +33375600,7.69034 +33463200,7.66833 +33550800,7.64604 +33638400,7.62417 +33726000,7.602 +33813600,7.58026 +33901200,7.55821 +33988800,7.5366 +34076400,7.51468 +34164000,7.49319 +34251600,7.4714 +34339200,7.45003 +34426800,7.42837 +34514400,7.40712 +34602000,7.38559 +34689600,7.36446 +34777200,7.34305 +34864800,7.32204 +34952400,7.30076 +35040000,7.27987 +35127600,7.25871 +35215200,7.23794 +35302800,7.2169 +35390400,7.19626 +35478000,7.17533 +35565600,7.15481 +35653200,7.134 +35740800,7.11361 +35828400,7.09291 +35916000,7.07264 +36003600,7.05205 +36091200,7.03191 +36178800,7.01143 +36266400,6.99141 +36354000,6.97105 +36441600,6.95114 +36529200,6.9309 +36616800,6.9111 +36704400,6.89098 +36792000,6.8713 +36879600,6.85129 +36967200,6.83173 +37054800,6.81182 +37142400,6.79239 +37230000,6.77258 +37317600,6.75327 +37405200,6.73357 +37492800,6.71438 +37580400,6.69479 +37668000,6.6757 +37755600,6.65623 +37843200,6.63726 +37930800,6.61789 +38018400,6.59903 +38106000,6.57977 +38193600,6.56103 +38281200,6.54187 +38368800,6.52324 +38456400,6.50419 +38544000,6.48567 +38631600,6.46673 +38719200,6.44832 +38806800,6.42948 +38894400,6.41118 +38982000,6.39245 +39069600,6.37425 +39157200,6.35564 +39244800,6.33753 +39332400,6.31904 +39420000,6.30103 +39507600,6.28264 +39595200,6.26474 +39682800,6.24645 +39770400,6.22866 +39858000,6.21047 +39945600,6.19279 +40033200,6.1747 +40120800,6.15712 +40208400,6.13914 +40296000,6.12166 +40383600,6.10378 +40471200,6.0864 +40558800,6.06862 +40646400,6.05135 +40734000,6.03367 +40821600,6.01649 +40909200,5.99892 +40996800,5.98184 +41084400,5.96437 +41172000,5.94739 +41259600,5.93001 +41347200,5.91314 +41434800,5.89585 +41522400,5.87909 +41610000,5.86189 +41697600,5.84523 +41785200,5.82813 +41872800,5.81156 +41960400,5.79456 +42048000,5.77809 +42135600,5.76119 +42223200,5.74481 +42310800,5.728 +42398400,5.71173 +42486000,5.69501 +42573600,5.67883 +42661200,5.66221 +42748800,5.64612 +42836400,5.6296 +42924000,5.6136 +43011600,5.59718 +43099200,5.58126 +43186800,5.56495 +43274400,5.54911 +43362000,5.5329 +43449600,5.51715 +43537200,5.50103 +43624800,5.48538 +43712400,5.46934 +43800000,5.45379 +43887600,5.43784 +43975200,5.42238 +44062800,5.40652 +44150400,5.39115 +44238000,5.37538 +44325600,5.3601 +44413200,5.34441 +44500800,5.32923 +44588400,5.31363 +44676000,5.29854 +44763600,5.28302 +44851200,5.26803 +44938800,5.25259 +45026400,5.23769 +45114000,5.22233 +45201600,5.20753 +45289200,5.19225 +45376800,5.17754 +45464400,5.16234 +45552000,5.14772 +45639600,5.13261 +45727200,5.11807 +45814800,5.10305 +45902400,5.08859 +45990000,5.07366 +46077600,5.05928 +46165200,5.04444 +46252800,5.03014 +46340400,5.01538 +46428000,5.00117 +46515600,4.98649 +46603200,4.97237 +46690800,4.95777 +46778400,4.94373 +46866000,4.92922 +46953600,4.91525 +47041200,4.90083 +47128800,4.88694 +47216400,4.87261 +47304000,4.85879 +47391600,4.84455 +47479200,4.8308 +47566800,4.81665 +47654400,4.80298 +47742000,4.7889 +47829600,4.77532 +47917200,4.76132 +48004800,4.74781 +48092400,4.7339 +48180000,4.72047 +48267600,4.70663 +48355200,4.69328 +48442800,4.67952 +48530400,4.66625 +48618000,4.65257 +48705600,4.63938 +48793200,4.62577 +48880800,4.61266 +48968400,4.59912 +49056000,4.5861 +49143600,4.57263 +49231200,4.55969 +49318800,4.54629 +49406400,4.53343 +49494000,4.5201 +49581600,4.50732 +49669200,4.49407 +49756800,4.48136 +49844400,4.46818 +49932000,4.45555 +50019600,4.44245 +50107200,4.42989 +50194800,4.41686 +50282400,4.40437 +50370000,4.39143 +50457600,4.379 +50545200,4.36613 +50632800,4.35378 +50720400,4.34099 +50808000,4.3287 +50895600,4.31599 +50983200,4.30377 +51070800,4.29113 +51158400,4.27898 +51246000,4.26642 +51333600,4.25433 +51421200,4.24185 +51508800,4.22983 +51596400,4.21741 +51684000,4.20547 +51771600,4.19312 +51859200,4.18125 +51946800,4.16897 +52034400,4.15717 +52122000,4.14495 +52209600,4.13323 +52297200,4.12108 +52384800,4.10942 +52472400,4.09735 +52560000,4.08575 +52647600,4.07375 +52735200,4.06222 +52822800,4.05028 +52910400,4.03883 +52998000,4.02695 +53085600,4.01557 +53173200,4.00375 +53260800,3.99245 +53348400,3.98069 +53436000,3.96945 +53523600,3.95776 +53611200,3.94659 +53698800,3.93497 +53786400,3.92386 +53874000,3.9123 +53961600,3.90126 +54049200,3.88977 +54136800,3.87879 +54224400,3.86737 +54312000,3.85645 +54399600,3.84509 +54487200,3.83424 +54574800,3.82294 +54662400,3.81216 +54750000,3.80092 +54837600,3.79021 +54925200,3.77902 +55012800,3.76838 +55100400,3.75726 +55188000,3.74667 +55275600,3.73562 +55363200,3.72509 +55450800,3.71411 +55538400,3.70363 +55626000,3.69272 +55713600,3.6823 +55801200,3.67145 +55888800,3.66109 +55976400,3.65031 +56064000,3.64 +56151600,3.62929 +56239200,3.61903 +56326800,3.60839 +56414400,3.59819 +56502000,3.5876 +56589600,3.57747 +56677200,3.56693 +56764800,3.55687 +56852400,3.54639 +56940000,3.53638 +57027600,3.52596 +57115200,3.51602 +57202800,3.50565 +57290400,3.49577 +57378000,3.48546 +57465600,3.47563 +57553200,3.46539 +57640800,3.45561 +57728400,3.44543 +57816000,3.43571 +57903600,3.42558 +57991200,3.41593 +58078800,3.40584 +58166400,3.39626 +58254000,3.38622 +58341600,3.3767 +58429200,3.36672 +58516800,3.35725 +58604400,3.34733 +58692000,3.33791 +58779600,3.32805 +58867200,3.31869 +58954800,3.30888 +59042400,3.29958 +59130000,3.28982 +59217600,3.28058 +59305200,3.27087 +59392800,3.26168 +59480400,3.25203 +59568000,3.2429 +59655600,3.2333 +59743200,3.22422 +59830800,3.21468 +59918400,3.20565 +60006000,3.19616 +60093600,3.18719 +60181200,3.17775 +60268800,3.16883 +60356400,3.15945 +60444000,3.15058 +60531600,3.14125 +60619200,3.13244 +60706800,3.12315 +60794400,3.1144 +60882000,3.10516 +60969600,3.09647 +61057200,3.08727 +61144800,3.07864 +61232400,3.06949 +61320000,3.06091 +61407600,3.05181 +61495200,3.04328 +61582800,3.03423 +61670400,3.02575 +61758000,3.01676 +61845600,3.00832 +61933200,2.99938 +62020800,2.991 +62108400,2.9821 +62196000,2.97378 +62283600,2.96492 +62371200,2.95665 +62458800,2.94784 +62546400,2.93963 +62634000,2.93086 +62721600,2.9227 +62809200,2.91398 +62896800,2.90586 +62984400,2.8972 +63072000,2.88912 +63159600,2.88052 +63247200,2.87248 +63334800,2.86392 +63422400,2.85594 +63510000,2.84743 +63597600,2.83949 +63685200,2.83103 +63772800,2.82313 +63860400,2.81473 +63948000,2.80687 +64035600,2.79852 +64123200,2.7907 +64210800,2.7824 +64298400,2.77463 +64386000,2.76637 +64473600,2.75865 +64561200,2.75044 +64648800,2.74276 +64736400,2.7346 +64824000,2.72696 +64911600,2.71885 +64999200,2.71126 +65086800,2.70319 +65174400,2.69564 +65262000,2.68762 +65349600,2.68012 +65437200,2.67213 +65524800,2.66469 +65612400,2.65674 +65700000,2.64934 +65787600,2.64144 +65875200,2.63408 +65962800,2.62623 +66050400,2.61891 +66138000,2.6111 +66225600,2.60383 +66313200,2.59606 +66400800,2.58883 +66488400,2.58111 +66576000,2.57392 +66663600,2.56624 +66751200,2.5591 +66838800,2.55146 +66926400,2.54436 +67014000,2.53676 +67101600,2.52971 +67189200,2.52215 +67276800,2.51514 +67364400,2.50762 +67452000,2.50065 +67539600,2.49318 +67627200,2.48625 +67714800,2.47882 +67802400,2.47193 +67890000,2.46454 +67977600,2.45769 +68065200,2.45035 +68152800,2.44353 +68240400,2.43624 +68328000,2.42946 +68415600,2.42221 +68503200,2.41546 +68590800,2.40826 +68678400,2.40155 +68766000,2.39439 +68853600,2.38772 +68941200,2.3806 +69028800,2.37396 +69116400,2.36689 +69204000,2.36029 +69291600,2.35326 +69379200,2.34669 +69466800,2.33971 +69554400,2.33317 +69642000,2.32624 +69729600,2.31973 +69817200,2.31284 +69904800,2.30637 +69992400,2.29952 +70080000,2.29309 +70167600,2.28627 +70255200,2.27989 +70342800,2.2731 +70430400,2.26676 +70518000,2.26 +70605600,2.25371 +70693200,2.24698 +70780800,2.24073 +70868400,2.23404 +70956000,2.22782 +71043600,2.22118 +71131200,2.21499 +71218800,2.20838 +71306400,2.20223 +71394000,2.19567 +71481600,2.18954 +71569200,2.18302 +71656800,2.17693 +71744400,2.17045 +71832000,2.16439 +71919600,2.15795 +72007200,2.15193 +72094800,2.14552 +72182400,2.13953 +72270000,2.13317 +72357600,2.1272 +72445200,2.12089 +72532800,2.11495 +72620400,2.10867 +72708000,2.10277 +72795600,2.09652 +72883200,2.09066 +72970800,2.08445 +73058400,2.07862 +73146000,2.07244 +73233600,2.06665 +73321200,2.0605 +73408800,2.05475 +73496400,2.04863 +73584000,2.04292 +73671600,2.03683 +73759200,2.03115 +73846800,2.0251 +73934400,2.01945 +74022000,2.01344 +74109600,2.00782 +74197200,2.00184 +74284800,1.99626 +74372400,1.99031 +74460000,1.98476 +74547600,1.97884 +74635200,1.97333 +74722800,1.96745 +74810400,1.96196 +74898000,1.95612 +74985600,1.95066 +75073200,1.94485 +75160800,1.93943 +75248400,1.93365 +75336000,1.92826 +75423600,1.92251 +75511200,1.91715 +75598800,1.91144 +75686400,1.90611 +75774000,1.90043 +75861600,1.89513 +75949200,1.88948 +76036800,1.88422 +76124400,1.8786 +76212000,1.87336 +76299600,1.86778 +76387200,1.86257 +76474800,1.85703 +76562400,1.85184 +76650000,1.84633 +76737600,1.84118 +76825200,1.83569 +76912800,1.83058 +77000400,1.82512 +77088000,1.82003 +77175600,1.81461 +77263200,1.80955 +77350800,1.80416 +77438400,1.79913 +77526000,1.79376 +77613600,1.78877 +77701200,1.78343 +77788800,1.77847 +77876400,1.77316 +77964000,1.76822 +78051600,1.76295 +78139200,1.75804 +78226800,1.75279 +78314400,1.74792 +78402000,1.74269 +78489600,1.73785 +78577200,1.73266 +78664800,1.72784 +78752400,1.72268 +78840000,1.71789 +78927600,1.71275 +79015200,1.708 +79102800,1.70288 +79190400,1.69817 +79278000,1.69307 +79365600,1.68839 +79453200,1.68332 +79540800,1.67866 +79628400,1.67363 +79716000,1.66899 +79803600,1.66399 +79891200,1.65938 +79978800,1.6544 +80066400,1.64983 +80154000,1.64487 +80241600,1.64033 +80329200,1.63539 +80416800,1.63088 +80504400,1.62597 +80592000,1.62149 +80679600,1.61661 +80767200,1.61215 +80854800,1.6073 +80942400,1.60286 +81030000,1.59804 +81117600,1.59363 +81205200,1.58884 +81292800,1.58445 +81380400,1.57969 +81468000,1.57532 +81555600,1.57059 +81643200,1.56625 +81730800,1.56154 +81818400,1.55723 +81906000,1.55255 +81993600,1.54826 +82081200,1.54361 +82168800,1.53934 +82256400,1.53472 +82344000,1.53048 +82431600,1.52588 +82519200,1.52166 +82606800,1.51709 +82694400,1.5129 +82782000,1.50835 +82869600,1.50419 +82957200,1.49966 +83044800,1.49553 +83132400,1.49102 +83220000,1.48692 +83307600,1.48243 +83395200,1.47835 +83482800,1.47389 +83570400,1.46984 +83658000,1.4654 +83745600,1.46138 +83833200,1.45696 +83920800,1.45296 +84008400,1.44857 +84096000,1.44459 +84183600,1.44023 +84271200,1.43627 +84358800,1.43193 +84446400,1.428 +84534000,1.42368 +84621600,1.41978 +84709200,1.41548 +84796800,1.4116 +84884400,1.40733 +84972000,1.40347 +85059600,1.39922 +85147200,1.39539 +85234800,1.39116 +85322400,1.38735 +85410000,1.38315 +85497600,1.37936 +85585200,1.37518 +85672800,1.37142 +85760400,1.36726 +85848000,1.36352 +85935600,1.35938 +86023200,1.35567 +86110800,1.35155 +86198400,1.34786 +86286000,1.34377 +86373600,1.3401 +86461200,1.33602 +86548800,1.33239 +86636400,1.32832 +86724000,1.32472 +86811600,1.32067 +86899200,1.31709 +86986800,1.31306 +87074400,1.30951 +87162000,1.30549 +87249600,1.30197 +87337200,1.29797 +87424800,1.29447 +87512400,1.2905 +87600000,1.28701 +87687600,1.28307 +87775200,1.2796 +87862800,1.27568 +87950400,1.27223 +88038000,1.26833 +88125600,1.2649 +88213200,1.26103 +88300800,1.25761 +88388400,1.25377 +88476000,1.25036 +88563600,1.24655 +88651200,1.24316 +88738800,1.23937 +88826400,1.236 +88914000,1.23223 +89001600,1.22888 +89089200,1.22514 +89176800,1.2218 +89264400,1.21808 +89352000,1.21477 +89439600,1.21106 +89527200,1.20777 +89614800,1.20409 +89702400,1.20081 +89790000,1.19716 +89877600,1.19389 +89965200,1.19027 +90052800,1.18701 +90140400,1.18341 +90228000,1.18018 +90315600,1.17659 +90403200,1.17338 +90490800,1.16982 +90578400,1.16662 +90666000,1.16308 +90753600,1.1599 +90841200,1.15638 +90928800,1.15322 +91016400,1.14972 +91104000,1.14658 +91191600,1.1431 +91279200,1.14164 +91366800,1.13651 +91454400,1.13342 +91542000,1.12996 +91629600,1.12689 +91717200,1.12345 +91804800,1.1204 +91892400,1.11698 +91980000,1.11395 +92067600,1.11055 +92155200,1.10753 +92242800,1.10415 +92330400,1.10115 +92418000,1.0978 +92505600,1.0948 +92593200,1.09148 +92680800,1.08849 +92768400,1.0852 +92856000,1.08222 +92943600,1.07895 +93031200,1.07599 +93118800,1.07273 +93206400,1.06979 +93294000,1.06656 +93381600,1.06362 +93469200,1.06042 +93556800,1.0575 +93644400,1.05431 +93732000,1.05141 +93819600,1.04823 +93907200,1.04536 +93994800,1.04219 +94082400,1.03934 +94170000,1.03619 +94257600,1.03335 +94345200,1.03022 +94432800,1.0274 +94520400,1.02429 +94608000,1.02148 +94695600,1.01839 +94783200,1.0156 +94870800,1.01252 +94958400,1.00975 +95046000,1.00669 +95133600,1.00394 +95221200,1.00089 +95308800,1 +95396400,1 +95484000,1 +95571600,1 +95659200,1 +95746800,1 +95834400,1 +95922000,1 +96009600,1 +96097200,1 +96184800,1 +96272400,1 +96360000,1 +96447600,1 +96535200,1 +96622800,1 +96710400,1 +96798000,1 +96885600,1 +96973200,1 +97060800,1 +97148400,1 +97236000,1 +97323600,1 +97411200,1 +97498800,1 +97586400,1 +97674000,1 +97761600,1 +97849200,1 +97936800,1 +98024400,1 +98112000,1 +98199600,1 +98287200,1 +98374800,1 +98462400,1 +98550000,1 +98637600,1 +98725200,1 +98812800,1 +98900400,1 +98988000,1 +99075600,1 +99163200,1 +99250800,1 +99338400,1 +99426000,1 +99513600,1 +99601200,1 +99688800,1 +99776400,1 +99864000,1 +99951600,1 +100039200,1 +100126800,1 +100214400,1 +100302000,1 +100389600,1 +100477200,1 +100564800,1 +100652400,1 +100740000,1 +100827600,1 +100915200,1 +101002800,1 +101090400,1 +101178000,1 +101265600,1 +101353200,1 +101440800,1 +101528400,1 +101616000,1 +101703600,1 +101791200,1 +101878800,1 +101966400,1 +102054000,1 +102141600,1 +102229200,1 +102316800,1 +102404400,1 +102492000,1 +102579600,1 +102667200,1 +102754800,1 +102842400,1 +102930000,1 +103017600,1 +103105200,1 +103192800,1 +103280400,1 +103368000,1 +103455600,1 +103543200,1 +103630800,1 +103718400,1 +103806000,1 +103893600,1 +103981200,1 +104068800,1 +104156400,1 +104244000,1 +104331600,1 +104419200,1 +104506800,1 +104594400,1 +104682000,1 +104769600,1 +104857200,1 +104944800,1 +105032400,1 +105120000,1 +105207600,1 +105295200,1 +105382800,1 +105470400,1 +105558000,1 +105645600,1 +105733200,1 +105820800,1 +105908400,1 +105996000,1 +106083600,1 +106171200,1 +106258800,1 +106346400,1 +106434000,1 +106521600,1 +106609200,1 +106696800,1 +106784400,1 +106872000,1 +106959600,1 +107047200,1 +107134800,1 +107222400,1 +107310000,1 +107397600,1 +107485200,1 +107572800,1 +107660400,1 +107748000,1 +107835600,1 +107923200,1 +108010800,1 +108098400,1 +108186000,1 +108273600,1 +108361200,1 +108448800,1 +108536400,1 +108624000,1 +108711600,1 +108799200,1 +108886800,1 +108974400,1 +109062000,1 +109149600,1 +109237200,1 +109324800,1 +109412400,1 +109500000,1 +109587600,1 +109675200,1 +109762800,1 +109850400,1 +109938000,1 +110025600,1 +110113200,1 +110200800,1 +110288400,1 +110376000,1 +110463600,1 +110551200,1 +110638800,1 +110726400,1 +110814000,1 +110901600,1 +110989200,1 +111076800,1 +111164400,1 +111252000,1 +111339600,1 +111427200,1 +111514800,1 +111602400,1 +111690000,1 +111777600,1 +111865200,1 +111952800,1 +112040400,1 +112128000,1 +112215600,1 +112303200,1 +112390800,1 +112478400,1 +112566000,1 +112653600,1 +112741200,1 +112828800,1 +112916400,1 +113004000,1 +113091600,1 +113179200,1 +113266800,1 +113354400,1 +113442000,1 +113529600,1 +113617200,1 +113704800,1 +113792400,1 +113880000,1 +113967600,1 +114055200,1 +114142800,1 +114230400,1 +114318000,1 +114405600,1 +114493200,1 +114580800,1 +114668400,1 +114756000,1 +114843600,1 +114931200,1 +115018800,1 +115106400,1 +115194000,1 +115281600,1 +115369200,1 +115456800,1 +115544400,1 +115632000,1 +115719600,1 +115807200,1 +115894800,1 +115982400,1 +116070000,1 +116157600,1 +116245200,1 +116332800,1 +116420400,1 +116508000,1 +116595600,1 +116683200,1 +116770800,1 +116858400,1 +116946000,1 +117033600,1 +117121200,1 +117208800,1 +117296400,1 +117384000,1 +117471600,1 +117559200,1 +117646800,1 +117734400,1 +117822000,1 +117909600,1 +117997200,1 +118084800,1 +118172400,1 +118260000,1 +118347600,1 +118435200,1 +118522800,1 +118610400,1 +118698000,1 +118785600,1 +118873200,1 +118960800,1 +119048400,1 +119136000,1 +119223600,1 +119311200,1 +119398800,1 +119486400,1 +119574000,1 +119661600,1 +119749200,1 +119836800,1 +119924400,1 +120012000,1 +120099600,1 +120187200,1 +120274800,1 +120362400,1 +120450000,1 +120537600,1 +120625200,1 +120712800,1 +120800400,1 +120888000,1 +120975600,1 +121063200,1 +121150800,1 +121238400,1 +121326000,1 +121413600,1 +121501200,1 +121588800,1 +121676400,1 +121764000,1 +121851600,1 +121939200,1 +122026800,1 +122114400,1 +122202000,1 +122289600,1 +122377200,1 +122464800,1 +122552400,1 +122640000,1 +122727600,1 +122815200,1 +122902800,1 +122990400,1 +123078000,1 +123165600,1 +123253200,1 +123340800,1 +123428400,1 +123516000,1 +123603600,1 +123691200,1 +123778800,1 +123866400,1 +123954000,1 +124041600,1 +124129200,1 +124216800,1 +124304400,1 +124392000,1 +124479600,1 +124567200,1 +124654800,1 +124742400,1 +124830000,1 +124917600,1 +125005200,1 +125092800,1 +125180400,1 +125268000,1 +125355600,1 +125443200,1 +125530800,1 +125538055,1 +125538056,0.023999333 +125538057,0 + diff --git a/csvs/token_payments.csv b/csvs/token_payments.csv new file mode 100644 index 00000000..11fb7dc4 --- /dev/null +++ b/csvs/token_payments.csv @@ -0,0 +1,1319 @@ +Coinlist Non-US_0,2019-12-01T00:00:00Z,Coinlist Non-US,10000000,0 +CS2_C0_1,2019-12-01T00:00:00Z,CS2_C0,5000000,0 +SA <1>_2,2020-01-01T00:00:00Z,SA <1>,16000,0 +SA <2>_3,2020-01-01T00:00:00Z,SA <2>,160000,0 +SA <3>_4,2020-01-01T00:00:00Z,SA <3>,800,0 +SA <4>_5,2020-01-01T00:00:00Z,SA <4>,8000,0 +SA <5>_6,2020-01-01T00:00:00Z,SA <5>,16000,0 +SA <6>_7,2020-01-01T00:00:00Z,SA <6>,4000,0 +SA <7>_8,2020-01-01T00:00:00Z,SA <7>,3200,0 +SA <8>_9,2020-01-01T00:00:00Z,SA <8>,4000,0 +SA <9>_10,2020-01-01T00:00:00Z,SA <9>,8000,0 +SA <10>_11,2020-01-01T00:00:00Z,SA <10>,4000,0 +SA <11>_12,2020-01-01T00:00:00Z,SA <11>,4000,0 +SA <12>_13,2020-01-01T00:00:00Z,SA <12>,4000,0 +SA <13>_14,2020-01-01T00:00:00Z,SA <13>,80000,0 +SA <14>_15,2020-01-01T00:00:00Z,SA <14>,8000,0 +SA <15> _16,2020-01-01T00:00:00Z,SA <15> ,1600,0 +SA <16>_17,2020-01-01T00:00:00Z,SA <16>,4000,0 +SA <17>_18,2020-01-01T00:00:00Z,SA <17>,4000,0 +SA <18>_19,2020-01-01T00:00:00Z,SA <18>,8000,0 +SA <19>_20,2020-01-01T00:00:00Z,SA <19>,6400,0 +SA <20>_21,2020-01-01T00:00:00Z,SA <20>,16000,0 +SB <1>_22,2020-01-01T00:00:00Z,SB <1>,5333.33,0 +SB <2>_23,2020-01-01T00:00:00Z,SB <2>,3200,0 +SB <3>_24,2020-01-01T00:00:00Z,SB <3>,10666.67,0 +SB <4>_25,2020-01-01T00:00:00Z,SB <4>,106666.67,0 +SB <5>_26,2020-01-01T00:00:00Z,SB <5>,53333.33,0 +SB <6>_27,2020-01-01T00:00:00Z,SB <6>,5333.33,0 +SB <7>_28,2020-01-01T00:00:00Z,SB <7>,2890.67,0 +SB <8>_29,2020-01-01T00:00:00Z,SB <8>,97902.93,0 +SB <9>_30,2020-01-01T00:00:00Z,SB <9>,32030.72,0 +SB <10>_31,2020-01-01T00:00:00Z,SB <10>,105450.6,0 +SB <11>_32,2020-01-01T00:00:00Z,SB <11>,28844.46,0 +SB <12>_33,2020-01-01T00:00:00Z,SB <12>,8267.36,0 +SB <13>_34,2020-01-01T00:00:00Z,SB <13>,28288.88,0 +SB <14>_35,2020-01-01T00:00:00Z,SB <14>,16000,0 +SB <15>_36,2020-01-01T00:00:00Z,SB <15>,10666.67,0 +SB <16>_37,2020-01-01T00:00:00Z,SB <16>,26666.67,0 +SB <17>_38,2020-01-01T00:00:00Z,SB <17>,21333.33,0 +SB <18>_39,2020-01-01T00:00:00Z,SB <18>,26666.67,0 +SB <19>_40,2020-01-01T00:00:00Z,SB <19>,53333.33,0 +SB <20>_41,2020-01-01T00:00:00Z,SB <20>,30880,0 +SB <21>_42,2020-01-01T00:00:00Z,SB <21>,10491.95,0 +SB <22>_43,2020-01-01T00:00:00Z,SB <22>,10666.67,0 +SB <23>_44,2020-01-01T00:00:00Z,SB <23>,2666.67,0 +SB <24>_45,2020-01-01T00:00:00Z,SB <24>,2180.66,0 +SB <25>_46,2020-01-01T00:00:00Z,SB <25>,10666.67,0 +SB <26>_47,2020-01-01T00:00:00Z,SB <26>,16832,0 +SB <27>_48,2020-01-01T00:00:00Z,SB <27>,108666.67,0 +SB <28>_49,2020-01-01T00:00:00Z,SB <28>,4800,0 +SB <29>_50,2020-01-01T00:00:00Z,SB <29>,53333.33,0 +SB <30>_51,2020-01-01T00:00:00Z,SB <30>,1066.67,0 +SB <31>_52,2020-01-01T00:00:00Z,SB <31>,16000,0 +SB <32>_53,2020-01-01T00:00:00Z,SB <32>,10666.67,0 +SB <33>_54,2020-01-01T00:00:00Z,SB <33>,10666.67,0 +SB <34>_55,2020-01-01T00:00:00Z,SB <34>,10666.67,0 +SB <35>_56,2020-01-01T00:00:00Z,SB <35>,50613.72,0 +SB <36>_57,2020-01-01T00:00:00Z,SB <36>,5642.99,0 +SB <37>_58,2020-01-01T00:00:00Z,SB <37>,2666.67,0 +SB <38>_59,2020-01-01T00:00:00Z,SB <38>,16000,0 +SB <39>_60,2020-01-01T00:00:00Z,SB <39>,8533.33,0 +SB <40>_61,2020-01-01T00:00:00Z,SB <40>,53760,0 +SB <41>_62,2020-01-01T00:00:00Z,SB <41>,48000,0 +SB <42>_63,2020-01-01T00:00:00Z,SB <42>,53333.33,0 +SB <43>_64,2020-01-01T00:00:00Z,SB <43>,26666.67,0 +SB <44>_65,2020-01-01T00:00:00Z,SB <44>,74666.67,0 +SB <45>_66,2020-01-01T00:00:00Z,SB <45>,44640,0 +SB <46>_67,2020-01-01T00:00:00Z,SB <46>,45595.9,0 +CS2_C0_68,2020-01-01T00:00:00Z,CS2_C0,5000000,0 +ST_C1_69,2020-01-01T00:00:00Z,ST_C1,1111472,1 +SA <1>_70,2020-02-01T00:00:00Z,SA <1>,16000,0 +SA <2>_71,2020-02-01T00:00:00Z,SA <2>,160000,0 +SA <3>_72,2020-02-01T00:00:00Z,SA <3>,800,0 +SA <4>_73,2020-02-01T00:00:00Z,SA <4>,8000,0 +SA <5>_74,2020-02-01T00:00:00Z,SA <5>,16000,0 +SA <6>_75,2020-02-01T00:00:00Z,SA <6>,4000,0 +SA <7>_76,2020-02-01T00:00:00Z,SA <7>,3200,0 +SA <8>_77,2020-02-01T00:00:00Z,SA <8>,4000,0 +SA <9>_78,2020-02-01T00:00:00Z,SA <9>,8000,0 +SA <10>_79,2020-02-01T00:00:00Z,SA <10>,4000,0 +SA <11>_80,2020-02-01T00:00:00Z,SA <11>,4000,0 +SA <12>_81,2020-02-01T00:00:00Z,SA <12>,4000,0 +SA <13>_82,2020-02-01T00:00:00Z,SA <13>,80000,0 +SA <14>_83,2020-02-01T00:00:00Z,SA <14>,8000,0 +SA <15> _84,2020-02-01T00:00:00Z,SA <15> ,1600,0 +SA <16>_85,2020-02-01T00:00:00Z,SA <16>,4000,0 +SA <17>_86,2020-02-01T00:00:00Z,SA <17>,4000,0 +SA <18>_87,2020-02-01T00:00:00Z,SA <18>,8000,0 +SA <19>_88,2020-02-01T00:00:00Z,SA <19>,6400,0 +SA <20>_89,2020-02-01T00:00:00Z,SA <20>,16000,0 +SB <1>_90,2020-02-01T00:00:00Z,SB <1>,5333.33,0 +SB <2>_91,2020-02-01T00:00:00Z,SB <2>,3200,0 +SB <3>_92,2020-02-01T00:00:00Z,SB <3>,10666.67,0 +SB <4>_93,2020-02-01T00:00:00Z,SB <4>,106666.67,0 +SB <5>_94,2020-02-01T00:00:00Z,SB <5>,53333.33,0 +SB <6>_95,2020-02-01T00:00:00Z,SB <6>,5333.33,0 +SB <7>_96,2020-02-01T00:00:00Z,SB <7>,2890.67,0 +SB <8>_97,2020-02-01T00:00:00Z,SB <8>,97902.93,0 +SB <9>_98,2020-02-01T00:00:00Z,SB <9>,32030.72,0 +SB <10>_99,2020-02-01T00:00:00Z,SB <10>,105450.6,0 +SB <11>_100,2020-02-01T00:00:00Z,SB <11>,28844.46,0 +SB <12>_101,2020-02-01T00:00:00Z,SB <12>,8267.36,0 +SB <13>_102,2020-02-01T00:00:00Z,SB <13>,28288.88,0 +SB <14>_103,2020-02-01T00:00:00Z,SB <14>,16000,0 +SB <15>_104,2020-02-01T00:00:00Z,SB <15>,10666.67,0 +SB <16>_105,2020-02-01T00:00:00Z,SB <16>,26666.67,0 +SB <17>_106,2020-02-01T00:00:00Z,SB <17>,21333.33,0 +SB <18>_107,2020-02-01T00:00:00Z,SB <18>,26666.67,0 +SB <19>_108,2020-02-01T00:00:00Z,SB <19>,53333.33,0 +SB <20>_109,2020-02-01T00:00:00Z,SB <20>,30880,0 +SB <21>_110,2020-02-01T00:00:00Z,SB <21>,10491.95,0 +SB <22>_111,2020-02-01T00:00:00Z,SB <22>,10666.67,0 +SB <23>_112,2020-02-01T00:00:00Z,SB <23>,2666.67,0 +SB <24>_113,2020-02-01T00:00:00Z,SB <24>,2180.66,0 +SB <25>_114,2020-02-01T00:00:00Z,SB <25>,10666.67,0 +SB <26>_115,2020-02-01T00:00:00Z,SB <26>,16832,0 +SB <27>_116,2020-02-01T00:00:00Z,SB <27>,108666.67,0 +SB <28>_117,2020-02-01T00:00:00Z,SB <28>,4800,0 +SB <29>_118,2020-02-01T00:00:00Z,SB <29>,53333.33,0 +SB <30>_119,2020-02-01T00:00:00Z,SB <30>,1066.67,0 +SB <31>_120,2020-02-01T00:00:00Z,SB <31>,16000,0 +SB <32>_121,2020-02-01T00:00:00Z,SB <32>,10666.67,0 +SB <33>_122,2020-02-01T00:00:00Z,SB <33>,10666.67,0 +SB <34>_123,2020-02-01T00:00:00Z,SB <34>,10666.67,0 +SB <35>_124,2020-02-01T00:00:00Z,SB <35>,50613.72,0 +SB <36>_125,2020-02-01T00:00:00Z,SB <36>,5642.99,0 +SB <37>_126,2020-02-01T00:00:00Z,SB <37>,2666.67,0 +SB <38>_127,2020-02-01T00:00:00Z,SB <38>,16000,0 +SB <39>_128,2020-02-01T00:00:00Z,SB <39>,8533.33,0 +SB <40>_129,2020-02-01T00:00:00Z,SB <40>,53760,0 +SB <41>_130,2020-02-01T00:00:00Z,SB <41>,48000,0 +SB <42>_131,2020-02-01T00:00:00Z,SB <42>,53333.33,0 +SB <43>_132,2020-02-01T00:00:00Z,SB <43>,26666.67,0 +SB <44>_133,2020-02-01T00:00:00Z,SB <44>,74666.67,0 +SB <45>_134,2020-02-01T00:00:00Z,SB <45>,44640,0 +SB <46>_135,2020-02-01T00:00:00Z,SB <46>,45595.9,0 +FTS_C1_136,2020-02-01T00:00:00Z,FTS_C1,1000000,1 +CS1_C2_137,2020-02-01T00:00:00Z,CS1_C2,666667,2 +ST_C1_138,2020-02-01T00:00:00Z,ST_C1,1111472,1 +SA <1>_139,2020-03-01T00:00:00Z,SA <1>,16000,0 +SA <2>_140,2020-03-01T00:00:00Z,SA <2>,160000,0 +SA <3>_141,2020-03-01T00:00:00Z,SA <3>,800,0 +SA <4>_142,2020-03-01T00:00:00Z,SA <4>,8000,0 +SA <5>_143,2020-03-01T00:00:00Z,SA <5>,16000,0 +SA <6>_144,2020-03-01T00:00:00Z,SA <6>,4000,0 +SA <7>_145,2020-03-01T00:00:00Z,SA <7>,3200,0 +SA <8>_146,2020-03-01T00:00:00Z,SA <8>,4000,0 +SA <9>_147,2020-03-01T00:00:00Z,SA <9>,8000,0 +SA <10>_148,2020-03-01T00:00:00Z,SA <10>,4000,0 +SA <11>_149,2020-03-01T00:00:00Z,SA <11>,4000,0 +SA <12>_150,2020-03-01T00:00:00Z,SA <12>,4000,0 +SA <13>_151,2020-03-01T00:00:00Z,SA <13>,80000,0 +SA <14>_152,2020-03-01T00:00:00Z,SA <14>,8000,0 +SA <15> _153,2020-03-01T00:00:00Z,SA <15> ,1600,0 +SA <16>_154,2020-03-01T00:00:00Z,SA <16>,4000,0 +SA <17>_155,2020-03-01T00:00:00Z,SA <17>,4000,0 +SA <18>_156,2020-03-01T00:00:00Z,SA <18>,8000,0 +SA <19>_157,2020-03-01T00:00:00Z,SA <19>,6400,0 +SA <20>_158,2020-03-01T00:00:00Z,SA <20>,16000,0 +SB <1>_159,2020-03-01T00:00:00Z,SB <1>,5333.33,0 +SB <2>_160,2020-03-01T00:00:00Z,SB <2>,3200,0 +SB <3>_161,2020-03-01T00:00:00Z,SB <3>,10666.67,0 +SB <4>_162,2020-03-01T00:00:00Z,SB <4>,106666.67,0 +SB <5>_163,2020-03-01T00:00:00Z,SB <5>,53333.33,0 +SB <6>_164,2020-03-01T00:00:00Z,SB <6>,5333.33,0 +SB <7>_165,2020-03-01T00:00:00Z,SB <7>,2890.67,0 +SB <8>_166,2020-03-01T00:00:00Z,SB <8>,97902.93,0 +SB <9>_167,2020-03-01T00:00:00Z,SB <9>,32030.72,0 +SB <10>_168,2020-03-01T00:00:00Z,SB <10>,105450.6,0 +SB <11>_169,2020-03-01T00:00:00Z,SB <11>,28844.46,0 +SB <12>_170,2020-03-01T00:00:00Z,SB <12>,8267.36,0 +SB <13>_171,2020-03-01T00:00:00Z,SB <13>,28288.88,0 +SB <14>_172,2020-03-01T00:00:00Z,SB <14>,16000,0 +SB <15>_173,2020-03-01T00:00:00Z,SB <15>,10666.67,0 +SB <16>_174,2020-03-01T00:00:00Z,SB <16>,26666.67,0 +SB <17>_175,2020-03-01T00:00:00Z,SB <17>,21333.33,0 +SB <18>_176,2020-03-01T00:00:00Z,SB <18>,26666.67,0 +SB <19>_177,2020-03-01T00:00:00Z,SB <19>,53333.33,0 +SB <20>_178,2020-03-01T00:00:00Z,SB <20>,30880,0 +SB <21>_179,2020-03-01T00:00:00Z,SB <21>,10491.95,0 +SB <22>_180,2020-03-01T00:00:00Z,SB <22>,10666.67,0 +SB <23>_181,2020-03-01T00:00:00Z,SB <23>,2666.67,0 +SB <24>_182,2020-03-01T00:00:00Z,SB <24>,2180.66,0 +SB <25>_183,2020-03-01T00:00:00Z,SB <25>,10666.67,0 +SB <26>_184,2020-03-01T00:00:00Z,SB <26>,16832,0 +SB <27>_185,2020-03-01T00:00:00Z,SB <27>,108666.67,0 +SB <28>_186,2020-03-01T00:00:00Z,SB <28>,4800,0 +SB <29>_187,2020-03-01T00:00:00Z,SB <29>,53333.33,0 +SB <30>_188,2020-03-01T00:00:00Z,SB <30>,1066.67,0 +SB <31>_189,2020-03-01T00:00:00Z,SB <31>,16000,0 +SB <32>_190,2020-03-01T00:00:00Z,SB <32>,10666.67,0 +SB <33>_191,2020-03-01T00:00:00Z,SB <33>,10666.67,0 +SB <34>_192,2020-03-01T00:00:00Z,SB <34>,10666.67,0 +SB <35>_193,2020-03-01T00:00:00Z,SB <35>,50613.72,0 +SB <36>_194,2020-03-01T00:00:00Z,SB <36>,5642.99,0 +SB <37>_195,2020-03-01T00:00:00Z,SB <37>,2666.67,0 +SB <38>_196,2020-03-01T00:00:00Z,SB <38>,16000,0 +SB <39>_197,2020-03-01T00:00:00Z,SB <39>,8533.33,0 +SB <40>_198,2020-03-01T00:00:00Z,SB <40>,53760,0 +SB <41>_199,2020-03-01T00:00:00Z,SB <41>,48000,0 +SB <42>_200,2020-03-01T00:00:00Z,SB <42>,53333.33,0 +SB <43>_201,2020-03-01T00:00:00Z,SB <43>,26666.67,0 +SB <44>_202,2020-03-01T00:00:00Z,SB <44>,74666.67,0 +SB <45>_203,2020-03-01T00:00:00Z,SB <45>,44640,0 +SB <46>_204,2020-03-01T00:00:00Z,SB <46>,45595.9,0 +FTS_C1_205,2020-03-01T00:00:00Z,FTS_C1,1000000,1 +CS1_C2_206,2020-03-01T00:00:00Z,CS1_C2,666667,2 +ST_C1_207,2020-03-01T00:00:00Z,ST_C1,1111472,1 +SA <1>_208,2020-04-01T00:00:00Z,SA <1>,16000,0 +SA <2>_209,2020-04-01T00:00:00Z,SA <2>,160000,0 +SA <3>_210,2020-04-01T00:00:00Z,SA <3>,800,0 +SA <4>_211,2020-04-01T00:00:00Z,SA <4>,8000,0 +SA <5>_212,2020-04-01T00:00:00Z,SA <5>,16000,0 +SA <6>_213,2020-04-01T00:00:00Z,SA <6>,4000,0 +SA <7>_214,2020-04-01T00:00:00Z,SA <7>,3200,0 +SA <8>_215,2020-04-01T00:00:00Z,SA <8>,4000,0 +SA <9>_216,2020-04-01T00:00:00Z,SA <9>,8000,0 +SA <10>_217,2020-04-01T00:00:00Z,SA <10>,4000,0 +SA <11>_218,2020-04-01T00:00:00Z,SA <11>,4000,0 +SA <12>_219,2020-04-01T00:00:00Z,SA <12>,4000,0 +SA <13>_220,2020-04-01T00:00:00Z,SA <13>,80000,0 +SA <14>_221,2020-04-01T00:00:00Z,SA <14>,8000,0 +SA <15> _222,2020-04-01T00:00:00Z,SA <15> ,1600,0 +SA <16>_223,2020-04-01T00:00:00Z,SA <16>,4000,0 +SA <17>_224,2020-04-01T00:00:00Z,SA <17>,4000,0 +SA <18>_225,2020-04-01T00:00:00Z,SA <18>,8000,0 +SA <19>_226,2020-04-01T00:00:00Z,SA <19>,6400,0 +SA <20>_227,2020-04-01T00:00:00Z,SA <20>,16000,0 +SB <1>_228,2020-04-01T00:00:00Z,SB <1>,5333.33,0 +SB <2>_229,2020-04-01T00:00:00Z,SB <2>,3200,0 +SB <3>_230,2020-04-01T00:00:00Z,SB <3>,10666.67,0 +SB <4>_231,2020-04-01T00:00:00Z,SB <4>,106666.67,0 +SB <5>_232,2020-04-01T00:00:00Z,SB <5>,53333.33,0 +SB <6>_233,2020-04-01T00:00:00Z,SB <6>,5333.33,0 +SB <7>_234,2020-04-01T00:00:00Z,SB <7>,2890.67,0 +SB <8>_235,2020-04-01T00:00:00Z,SB <8>,97902.93,0 +SB <9>_236,2020-04-01T00:00:00Z,SB <9>,32030.72,0 +SB <10>_237,2020-04-01T00:00:00Z,SB <10>,105450.6,0 +SB <11>_238,2020-04-01T00:00:00Z,SB <11>,28844.46,0 +SB <12>_239,2020-04-01T00:00:00Z,SB <12>,8267.36,0 +SB <13>_240,2020-04-01T00:00:00Z,SB <13>,28288.88,0 +SB <14>_241,2020-04-01T00:00:00Z,SB <14>,16000,0 +SB <15>_242,2020-04-01T00:00:00Z,SB <15>,10666.67,0 +SB <16>_243,2020-04-01T00:00:00Z,SB <16>,26666.67,0 +SB <17>_244,2020-04-01T00:00:00Z,SB <17>,21333.33,0 +SB <18>_245,2020-04-01T00:00:00Z,SB <18>,26666.67,0 +SB <19>_246,2020-04-01T00:00:00Z,SB <19>,53333.33,0 +SB <20>_247,2020-04-01T00:00:00Z,SB <20>,30880,0 +SB <21>_248,2020-04-01T00:00:00Z,SB <21>,10491.95,0 +SB <22>_249,2020-04-01T00:00:00Z,SB <22>,10666.67,0 +SB <23>_250,2020-04-01T00:00:00Z,SB <23>,2666.67,0 +SB <24>_251,2020-04-01T00:00:00Z,SB <24>,2180.66,0 +SB <25>_252,2020-04-01T00:00:00Z,SB <25>,10666.67,0 +SB <26>_253,2020-04-01T00:00:00Z,SB <26>,16832,0 +SB <27>_254,2020-04-01T00:00:00Z,SB <27>,108666.67,0 +SB <28>_255,2020-04-01T00:00:00Z,SB <28>,4800,0 +SB <29>_256,2020-04-01T00:00:00Z,SB <29>,53333.33,0 +SB <30>_257,2020-04-01T00:00:00Z,SB <30>,1066.67,0 +SB <31>_258,2020-04-01T00:00:00Z,SB <31>,16000,0 +SB <32>_259,2020-04-01T00:00:00Z,SB <32>,10666.67,0 +SB <33>_260,2020-04-01T00:00:00Z,SB <33>,10666.67,0 +SB <34>_261,2020-04-01T00:00:00Z,SB <34>,10666.67,0 +SB <35>_262,2020-04-01T00:00:00Z,SB <35>,50613.72,0 +SB <36>_263,2020-04-01T00:00:00Z,SB <36>,5642.99,0 +SB <37>_264,2020-04-01T00:00:00Z,SB <37>,2666.67,0 +SB <38>_265,2020-04-01T00:00:00Z,SB <38>,16000,0 +SB <39>_266,2020-04-01T00:00:00Z,SB <39>,8533.33,0 +SB <40>_267,2020-04-01T00:00:00Z,SB <40>,53760,0 +SB <41>_268,2020-04-01T00:00:00Z,SB <41>,48000,0 +SB <42>_269,2020-04-01T00:00:00Z,SB <42>,53333.33,0 +SB <43>_270,2020-04-01T00:00:00Z,SB <43>,26666.67,0 +SB <44>_271,2020-04-01T00:00:00Z,SB <44>,74666.67,0 +SB <45>_272,2020-04-01T00:00:00Z,SB <45>,44640,0 +SB <46>_273,2020-04-01T00:00:00Z,SB <46>,45595.9,0 +FTS_C1_274,2020-04-01T00:00:00Z,FTS_C1,1000000,1 +CS1_C2_275,2020-04-01T00:00:00Z,CS1_C2,666667,2 +SA <1>_276,2020-05-01T00:00:00Z,SA <1>,16000,0 +SA <2>_277,2020-05-01T00:00:00Z,SA <2>,160000,0 +SA <3>_278,2020-05-01T00:00:00Z,SA <3>,800,0 +SA <4>_279,2020-05-01T00:00:00Z,SA <4>,8000,0 +SA <5>_280,2020-05-01T00:00:00Z,SA <5>,16000,0 +SA <6>_281,2020-05-01T00:00:00Z,SA <6>,4000,0 +SA <7>_282,2020-05-01T00:00:00Z,SA <7>,3200,0 +SA <8>_283,2020-05-01T00:00:00Z,SA <8>,4000,0 +SA <9>_284,2020-05-01T00:00:00Z,SA <9>,8000,0 +SA <10>_285,2020-05-01T00:00:00Z,SA <10>,4000,0 +SA <11>_286,2020-05-01T00:00:00Z,SA <11>,4000,0 +SA <12>_287,2020-05-01T00:00:00Z,SA <12>,4000,0 +SA <13>_288,2020-05-01T00:00:00Z,SA <13>,80000,0 +SA <14>_289,2020-05-01T00:00:00Z,SA <14>,8000,0 +SA <15> _290,2020-05-01T00:00:00Z,SA <15> ,1600,0 +SA <16>_291,2020-05-01T00:00:00Z,SA <16>,4000,0 +SA <17>_292,2020-05-01T00:00:00Z,SA <17>,4000,0 +SA <18>_293,2020-05-01T00:00:00Z,SA <18>,8000,0 +SA <19>_294,2020-05-01T00:00:00Z,SA <19>,6400,0 +SA <20>_295,2020-05-01T00:00:00Z,SA <20>,16000,0 +SB <1>_296,2020-05-01T00:00:00Z,SB <1>,5333.33,0 +SB <2>_297,2020-05-01T00:00:00Z,SB <2>,3200,0 +SB <3>_298,2020-05-01T00:00:00Z,SB <3>,10666.67,0 +SB <4>_299,2020-05-01T00:00:00Z,SB <4>,106666.67,0 +SB <5>_300,2020-05-01T00:00:00Z,SB <5>,53333.33,0 +SB <6>_301,2020-05-01T00:00:00Z,SB <6>,5333.33,0 +SB <7>_302,2020-05-01T00:00:00Z,SB <7>,2890.67,0 +SB <8>_303,2020-05-01T00:00:00Z,SB <8>,97902.93,0 +SB <9>_304,2020-05-01T00:00:00Z,SB <9>,32030.72,0 +SB <10>_305,2020-05-01T00:00:00Z,SB <10>,105450.6,0 +SB <11>_306,2020-05-01T00:00:00Z,SB <11>,28844.46,0 +SB <12>_307,2020-05-01T00:00:00Z,SB <12>,8267.36,0 +SB <13>_308,2020-05-01T00:00:00Z,SB <13>,28288.88,0 +SB <14>_309,2020-05-01T00:00:00Z,SB <14>,16000,0 +SB <15>_310,2020-05-01T00:00:00Z,SB <15>,10666.67,0 +SB <16>_311,2020-05-01T00:00:00Z,SB <16>,26666.67,0 +SB <17>_312,2020-05-01T00:00:00Z,SB <17>,21333.33,0 +SB <18>_313,2020-05-01T00:00:00Z,SB <18>,26666.67,0 +SB <19>_314,2020-05-01T00:00:00Z,SB <19>,53333.33,0 +SB <20>_315,2020-05-01T00:00:00Z,SB <20>,30880,0 +SB <21>_316,2020-05-01T00:00:00Z,SB <21>,10491.95,0 +SB <22>_317,2020-05-01T00:00:00Z,SB <22>,10666.67,0 +SB <23>_318,2020-05-01T00:00:00Z,SB <23>,2666.67,0 +SB <24>_319,2020-05-01T00:00:00Z,SB <24>,2180.66,0 +SB <25>_320,2020-05-01T00:00:00Z,SB <25>,10666.67,0 +SB <26>_321,2020-05-01T00:00:00Z,SB <26>,16832,0 +SB <27>_322,2020-05-01T00:00:00Z,SB <27>,108666.67,0 +SB <28>_323,2020-05-01T00:00:00Z,SB <28>,4800,0 +SB <29>_324,2020-05-01T00:00:00Z,SB <29>,53333.33,0 +SB <30>_325,2020-05-01T00:00:00Z,SB <30>,1066.67,0 +SB <31>_326,2020-05-01T00:00:00Z,SB <31>,16000,0 +SB <32>_327,2020-05-01T00:00:00Z,SB <32>,10666.67,0 +SB <33>_328,2020-05-01T00:00:00Z,SB <33>,10666.67,0 +SB <34>_329,2020-05-01T00:00:00Z,SB <34>,10666.67,0 +SB <35>_330,2020-05-01T00:00:00Z,SB <35>,50613.72,0 +SB <36>_331,2020-05-01T00:00:00Z,SB <36>,5642.99,0 +SB <37>_332,2020-05-01T00:00:00Z,SB <37>,2666.67,0 +SB <38>_333,2020-05-01T00:00:00Z,SB <38>,16000,0 +SB <39>_334,2020-05-01T00:00:00Z,SB <39>,8533.33,0 +SB <40>_335,2020-05-01T00:00:00Z,SB <40>,53760,0 +SB <41>_336,2020-05-01T00:00:00Z,SB <41>,48000,0 +SB <42>_337,2020-05-01T00:00:00Z,SB <42>,53333.33,0 +SB <43>_338,2020-05-01T00:00:00Z,SB <43>,26666.67,0 +SB <44>_339,2020-05-01T00:00:00Z,SB <44>,74666.67,0 +SB <45>_340,2020-05-01T00:00:00Z,SB <45>,44640,0 +SB <46>_341,2020-05-01T00:00:00Z,SB <46>,45595.9,0 +FTS_C1_342,2020-05-01T00:00:00Z,FTS_C1,1000000,1 +CS1_C2_343,2020-05-01T00:00:00Z,CS1_C2,666667,2 +SA <1>_344,2020-06-01T00:00:00Z,SA <1>,16000,0 +SA <2>_345,2020-06-01T00:00:00Z,SA <2>,160000,0 +SA <3>_346,2020-06-01T00:00:00Z,SA <3>,800,0 +SA <4>_347,2020-06-01T00:00:00Z,SA <4>,8000,0 +SA <5>_348,2020-06-01T00:00:00Z,SA <5>,16000,0 +SA <6>_349,2020-06-01T00:00:00Z,SA <6>,4000,0 +SA <7>_350,2020-06-01T00:00:00Z,SA <7>,3200,0 +SA <8>_351,2020-06-01T00:00:00Z,SA <8>,4000,0 +SA <9>_352,2020-06-01T00:00:00Z,SA <9>,8000,0 +SA <10>_353,2020-06-01T00:00:00Z,SA <10>,4000,0 +SA <11>_354,2020-06-01T00:00:00Z,SA <11>,4000,0 +SA <12>_355,2020-06-01T00:00:00Z,SA <12>,4000,0 +SA <13>_356,2020-06-01T00:00:00Z,SA <13>,80000,0 +SA <14>_357,2020-06-01T00:00:00Z,SA <14>,8000,0 +SA <15> _358,2020-06-01T00:00:00Z,SA <15> ,1600,0 +SA <16>_359,2020-06-01T00:00:00Z,SA <16>,4000,0 +SA <17>_360,2020-06-01T00:00:00Z,SA <17>,4000,0 +SA <18>_361,2020-06-01T00:00:00Z,SA <18>,8000,0 +SA <19>_362,2020-06-01T00:00:00Z,SA <19>,6400,0 +SA <20>_363,2020-06-01T00:00:00Z,SA <20>,16000,0 +SB <1>_364,2020-06-01T00:00:00Z,SB <1>,5333.33,0 +SB <2>_365,2020-06-01T00:00:00Z,SB <2>,3200,0 +SB <3>_366,2020-06-01T00:00:00Z,SB <3>,10666.67,0 +SB <4>_367,2020-06-01T00:00:00Z,SB <4>,106666.67,0 +SB <5>_368,2020-06-01T00:00:00Z,SB <5>,53333.33,0 +SB <6>_369,2020-06-01T00:00:00Z,SB <6>,5333.33,0 +SB <7>_370,2020-06-01T00:00:00Z,SB <7>,2890.67,0 +SB <8>_371,2020-06-01T00:00:00Z,SB <8>,97902.93,0 +SB <9>_372,2020-06-01T00:00:00Z,SB <9>,32030.72,0 +SB <10>_373,2020-06-01T00:00:00Z,SB <10>,105450.6,0 +SB <11>_374,2020-06-01T00:00:00Z,SB <11>,28844.46,0 +SB <12>_375,2020-06-01T00:00:00Z,SB <12>,8267.36,0 +SB <13>_376,2020-06-01T00:00:00Z,SB <13>,28288.88,0 +SB <14>_377,2020-06-01T00:00:00Z,SB <14>,16000,0 +SB <15>_378,2020-06-01T00:00:00Z,SB <15>,10666.67,0 +SB <16>_379,2020-06-01T00:00:00Z,SB <16>,26666.67,0 +SB <17>_380,2020-06-01T00:00:00Z,SB <17>,21333.33,0 +SB <18>_381,2020-06-01T00:00:00Z,SB <18>,26666.67,0 +SB <19>_382,2020-06-01T00:00:00Z,SB <19>,53333.33,0 +SB <20>_383,2020-06-01T00:00:00Z,SB <20>,30880,0 +SB <21>_384,2020-06-01T00:00:00Z,SB <21>,10491.95,0 +SB <22>_385,2020-06-01T00:00:00Z,SB <22>,10666.67,0 +SB <23>_386,2020-06-01T00:00:00Z,SB <23>,2666.67,0 +SB <24>_387,2020-06-01T00:00:00Z,SB <24>,2180.66,0 +SB <25>_388,2020-06-01T00:00:00Z,SB <25>,10666.67,0 +SB <26>_389,2020-06-01T00:00:00Z,SB <26>,16832,0 +SB <27>_390,2020-06-01T00:00:00Z,SB <27>,108666.67,0 +SB <28>_391,2020-06-01T00:00:00Z,SB <28>,4800,0 +SB <29>_392,2020-06-01T00:00:00Z,SB <29>,53333.33,0 +SB <30>_393,2020-06-01T00:00:00Z,SB <30>,1066.67,0 +SB <31>_394,2020-06-01T00:00:00Z,SB <31>,16000,0 +SB <32>_395,2020-06-01T00:00:00Z,SB <32>,10666.67,0 +SB <33>_396,2020-06-01T00:00:00Z,SB <33>,10666.67,0 +SB <34>_397,2020-06-01T00:00:00Z,SB <34>,10666.67,0 +SB <35>_398,2020-06-01T00:00:00Z,SB <35>,50613.72,0 +SB <36>_399,2020-06-01T00:00:00Z,SB <36>,5642.99,0 +SB <37>_400,2020-06-01T00:00:00Z,SB <37>,2666.67,0 +SB <38>_401,2020-06-01T00:00:00Z,SB <38>,16000,0 +SB <39>_402,2020-06-01T00:00:00Z,SB <39>,8533.33,0 +SB <40>_403,2020-06-01T00:00:00Z,SB <40>,53760,0 +SB <41>_404,2020-06-01T00:00:00Z,SB <41>,48000,0 +SB <42>_405,2020-06-01T00:00:00Z,SB <42>,53333.33,0 +SB <43>_406,2020-06-01T00:00:00Z,SB <43>,26666.67,0 +SB <44>_407,2020-06-01T00:00:00Z,SB <44>,74666.67,0 +SB <45>_408,2020-06-01T00:00:00Z,SB <45>,44640,0 +SB <46>_409,2020-06-01T00:00:00Z,SB <46>,45595.9,0 +FTS_C1_410,2020-06-01T00:00:00Z,FTS_C1,1000000,1 +CS1_C2_411,2020-06-01T00:00:00Z,CS1_C2,666667,2 +SA <1>_412,2020-07-01T00:00:00Z,SA <1>,16000,0 +SA <2>_413,2020-07-01T00:00:00Z,SA <2>,160000,0 +SA <3>_414,2020-07-01T00:00:00Z,SA <3>,800,0 +SA <4>_415,2020-07-01T00:00:00Z,SA <4>,8000,0 +SA <5>_416,2020-07-01T00:00:00Z,SA <5>,16000,0 +SA <6>_417,2020-07-01T00:00:00Z,SA <6>,4000,0 +SA <7>_418,2020-07-01T00:00:00Z,SA <7>,3200,0 +SA <8>_419,2020-07-01T00:00:00Z,SA <8>,4000,0 +SA <9>_420,2020-07-01T00:00:00Z,SA <9>,8000,0 +SA <10>_421,2020-07-01T00:00:00Z,SA <10>,4000,0 +SA <11>_422,2020-07-01T00:00:00Z,SA <11>,4000,0 +SA <12>_423,2020-07-01T00:00:00Z,SA <12>,4000,0 +SA <13>_424,2020-07-01T00:00:00Z,SA <13>,80000,0 +SA <14>_425,2020-07-01T00:00:00Z,SA <14>,8000,0 +SA <15> _426,2020-07-01T00:00:00Z,SA <15> ,1600,0 +SA <16>_427,2020-07-01T00:00:00Z,SA <16>,4000,0 +SA <17>_428,2020-07-01T00:00:00Z,SA <17>,4000,0 +SA <18>_429,2020-07-01T00:00:00Z,SA <18>,8000,0 +SA <19>_430,2020-07-01T00:00:00Z,SA <19>,6400,0 +SA <20>_431,2020-07-01T00:00:00Z,SA <20>,16000,0 +SB <1>_432,2020-07-01T00:00:00Z,SB <1>,5333.33,0 +SB <2>_433,2020-07-01T00:00:00Z,SB <2>,3200,0 +SB <3>_434,2020-07-01T00:00:00Z,SB <3>,10666.67,0 +SB <4>_435,2020-07-01T00:00:00Z,SB <4>,106666.67,0 +SB <5>_436,2020-07-01T00:00:00Z,SB <5>,53333.33,0 +SB <6>_437,2020-07-01T00:00:00Z,SB <6>,5333.33,0 +SB <7>_438,2020-07-01T00:00:00Z,SB <7>,2890.67,0 +SB <8>_439,2020-07-01T00:00:00Z,SB <8>,97902.93,0 +SB <9>_440,2020-07-01T00:00:00Z,SB <9>,32030.72,0 +SB <10>_441,2020-07-01T00:00:00Z,SB <10>,105450.6,0 +SB <11>_442,2020-07-01T00:00:00Z,SB <11>,28844.46,0 +SB <12>_443,2020-07-01T00:00:00Z,SB <12>,8267.36,0 +SB <13>_444,2020-07-01T00:00:00Z,SB <13>,28288.88,0 +SB <14>_445,2020-07-01T00:00:00Z,SB <14>,16000,0 +SB <15>_446,2020-07-01T00:00:00Z,SB <15>,10666.67,0 +SB <16>_447,2020-07-01T00:00:00Z,SB <16>,26666.67,0 +SB <17>_448,2020-07-01T00:00:00Z,SB <17>,21333.33,0 +SB <18>_449,2020-07-01T00:00:00Z,SB <18>,26666.67,0 +SB <19>_450,2020-07-01T00:00:00Z,SB <19>,53333.33,0 +SB <20>_451,2020-07-01T00:00:00Z,SB <20>,30880,0 +SB <21>_452,2020-07-01T00:00:00Z,SB <21>,10491.95,0 +SB <22>_453,2020-07-01T00:00:00Z,SB <22>,10666.67,0 +SB <23>_454,2020-07-01T00:00:00Z,SB <23>,2666.67,0 +SB <24>_455,2020-07-01T00:00:00Z,SB <24>,2180.66,0 +SB <25>_456,2020-07-01T00:00:00Z,SB <25>,10666.67,0 +SB <26>_457,2020-07-01T00:00:00Z,SB <26>,16832,0 +SB <27>_458,2020-07-01T00:00:00Z,SB <27>,108666.67,0 +SB <28>_459,2020-07-01T00:00:00Z,SB <28>,4800,0 +SB <29>_460,2020-07-01T00:00:00Z,SB <29>,53333.33,0 +SB <30>_461,2020-07-01T00:00:00Z,SB <30>,1066.67,0 +SB <31>_462,2020-07-01T00:00:00Z,SB <31>,16000,0 +SB <32>_463,2020-07-01T00:00:00Z,SB <32>,10666.67,0 +SB <33>_464,2020-07-01T00:00:00Z,SB <33>,10666.67,0 +SB <34>_465,2020-07-01T00:00:00Z,SB <34>,10666.67,0 +SB <35>_466,2020-07-01T00:00:00Z,SB <35>,50613.72,0 +SB <36>_467,2020-07-01T00:00:00Z,SB <36>,5642.99,0 +SB <37>_468,2020-07-01T00:00:00Z,SB <37>,2666.67,0 +SB <38>_469,2020-07-01T00:00:00Z,SB <38>,16000,0 +SB <39>_470,2020-07-01T00:00:00Z,SB <39>,8533.33,0 +SB <40>_471,2020-07-01T00:00:00Z,SB <40>,53760,0 +SB <41>_472,2020-07-01T00:00:00Z,SB <41>,48000,0 +SB <42>_473,2020-07-01T00:00:00Z,SB <42>,53333.33,0 +SB <43>_474,2020-07-01T00:00:00Z,SB <43>,26666.67,0 +SB <44>_475,2020-07-01T00:00:00Z,SB <44>,74666.67,0 +SB <45>_476,2020-07-01T00:00:00Z,SB <45>,44640,0 +SB <46>_477,2020-07-01T00:00:00Z,SB <46>,45595.9,0 +CS1_C2_478,2020-07-01T00:00:00Z,CS1_C2,666667,2 +SA <1>_479,2020-08-01T00:00:00Z,SA <1>,16000,0 +SA <2>_480,2020-08-01T00:00:00Z,SA <2>,160000,0 +SA <3>_481,2020-08-01T00:00:00Z,SA <3>,800,0 +SA <4>_482,2020-08-01T00:00:00Z,SA <4>,8000,0 +SA <5>_483,2020-08-01T00:00:00Z,SA <5>,16000,0 +SA <6>_484,2020-08-01T00:00:00Z,SA <6>,4000,0 +SA <7>_485,2020-08-01T00:00:00Z,SA <7>,3200,0 +SA <8>_486,2020-08-01T00:00:00Z,SA <8>,4000,0 +SA <9>_487,2020-08-01T00:00:00Z,SA <9>,8000,0 +SA <10>_488,2020-08-01T00:00:00Z,SA <10>,4000,0 +SA <11>_489,2020-08-01T00:00:00Z,SA <11>,4000,0 +SA <12>_490,2020-08-01T00:00:00Z,SA <12>,4000,0 +SA <13>_491,2020-08-01T00:00:00Z,SA <13>,80000,0 +SA <14>_492,2020-08-01T00:00:00Z,SA <14>,8000,0 +SA <15> _493,2020-08-01T00:00:00Z,SA <15> ,1600,0 +SA <16>_494,2020-08-01T00:00:00Z,SA <16>,4000,0 +SA <17>_495,2020-08-01T00:00:00Z,SA <17>,4000,0 +SA <18>_496,2020-08-01T00:00:00Z,SA <18>,8000,0 +SA <19>_497,2020-08-01T00:00:00Z,SA <19>,6400,0 +SA <20>_498,2020-08-01T00:00:00Z,SA <20>,16000,0 +SB <1>_499,2020-08-01T00:00:00Z,SB <1>,5333.33,0 +SB <2>_500,2020-08-01T00:00:00Z,SB <2>,3200,0 +SB <3>_501,2020-08-01T00:00:00Z,SB <3>,10666.67,0 +SB <4>_502,2020-08-01T00:00:00Z,SB <4>,106666.67,0 +SB <5>_503,2020-08-01T00:00:00Z,SB <5>,53333.33,0 +SB <6>_504,2020-08-01T00:00:00Z,SB <6>,5333.33,0 +SB <7>_505,2020-08-01T00:00:00Z,SB <7>,2890.67,0 +SB <8>_506,2020-08-01T00:00:00Z,SB <8>,97902.93,0 +SB <9>_507,2020-08-01T00:00:00Z,SB <9>,32030.72,0 +SB <10>_508,2020-08-01T00:00:00Z,SB <10>,105450.6,0 +SB <11>_509,2020-08-01T00:00:00Z,SB <11>,28844.46,0 +SB <12>_510,2020-08-01T00:00:00Z,SB <12>,8267.36,0 +SB <13>_511,2020-08-01T00:00:00Z,SB <13>,28288.88,0 +SB <14>_512,2020-08-01T00:00:00Z,SB <14>,16000,0 +SB <15>_513,2020-08-01T00:00:00Z,SB <15>,10666.67,0 +SB <16>_514,2020-08-01T00:00:00Z,SB <16>,26666.67,0 +SB <17>_515,2020-08-01T00:00:00Z,SB <17>,21333.33,0 +SB <18>_516,2020-08-01T00:00:00Z,SB <18>,26666.67,0 +SB <19>_517,2020-08-01T00:00:00Z,SB <19>,53333.33,0 +SB <20>_518,2020-08-01T00:00:00Z,SB <20>,30880,0 +SB <21>_519,2020-08-01T00:00:00Z,SB <21>,10491.95,0 +SB <22>_520,2020-08-01T00:00:00Z,SB <22>,10666.67,0 +SB <23>_521,2020-08-01T00:00:00Z,SB <23>,2666.67,0 +SB <24>_522,2020-08-01T00:00:00Z,SB <24>,2180.66,0 +SB <25>_523,2020-08-01T00:00:00Z,SB <25>,10666.67,0 +SB <26>_524,2020-08-01T00:00:00Z,SB <26>,16832,0 +SB <27>_525,2020-08-01T00:00:00Z,SB <27>,108666.67,0 +SB <28>_526,2020-08-01T00:00:00Z,SB <28>,4800,0 +SB <29>_527,2020-08-01T00:00:00Z,SB <29>,53333.33,0 +SB <30>_528,2020-08-01T00:00:00Z,SB <30>,1066.67,0 +SB <31>_529,2020-08-01T00:00:00Z,SB <31>,16000,0 +SB <32>_530,2020-08-01T00:00:00Z,SB <32>,10666.67,0 +SB <33>_531,2020-08-01T00:00:00Z,SB <33>,10666.67,0 +SB <34>_532,2020-08-01T00:00:00Z,SB <34>,10666.67,0 +SB <35>_533,2020-08-01T00:00:00Z,SB <35>,50613.72,0 +SB <36>_534,2020-08-01T00:00:00Z,SB <36>,5642.99,0 +SB <37>_535,2020-08-01T00:00:00Z,SB <37>,2666.67,0 +SB <38>_536,2020-08-01T00:00:00Z,SB <38>,16000,0 +SB <39>_537,2020-08-01T00:00:00Z,SB <39>,8533.33,0 +SB <40>_538,2020-08-01T00:00:00Z,SB <40>,53760,0 +SB <41>_539,2020-08-01T00:00:00Z,SB <41>,48000,0 +SB <42>_540,2020-08-01T00:00:00Z,SB <42>,53333.33,0 +SB <43>_541,2020-08-01T00:00:00Z,SB <43>,26666.67,0 +SB <44>_542,2020-08-01T00:00:00Z,SB <44>,74666.67,0 +SB <45>_543,2020-08-01T00:00:00Z,SB <45>,44640,0 +SB <46>_544,2020-08-01T00:00:00Z,SB <46>,45595.9,0 +CS1_C2_545,2020-08-01T00:00:00Z,CS1_C2,666667,2 +SA <1>_546,2020-09-01T00:00:00Z,SA <1>,16000,0 +SA <2>_547,2020-09-01T00:00:00Z,SA <2>,160000,0 +SA <3>_548,2020-09-01T00:00:00Z,SA <3>,800,0 +SA <4>_549,2020-09-01T00:00:00Z,SA <4>,8000,0 +SA <5>_550,2020-09-01T00:00:00Z,SA <5>,16000,0 +SA <6>_551,2020-09-01T00:00:00Z,SA <6>,4000,0 +SA <7>_552,2020-09-01T00:00:00Z,SA <7>,3200,0 +SA <8>_553,2020-09-01T00:00:00Z,SA <8>,4000,0 +SA <9>_554,2020-09-01T00:00:00Z,SA <9>,8000,0 +SA <10>_555,2020-09-01T00:00:00Z,SA <10>,4000,0 +SA <11>_556,2020-09-01T00:00:00Z,SA <11>,4000,0 +SA <12>_557,2020-09-01T00:00:00Z,SA <12>,4000,0 +SA <13>_558,2020-09-01T00:00:00Z,SA <13>,80000,0 +SA <14>_559,2020-09-01T00:00:00Z,SA <14>,8000,0 +SA <15> _560,2020-09-01T00:00:00Z,SA <15> ,1600,0 +SA <16>_561,2020-09-01T00:00:00Z,SA <16>,4000,0 +SA <17>_562,2020-09-01T00:00:00Z,SA <17>,4000,0 +SA <18>_563,2020-09-01T00:00:00Z,SA <18>,8000,0 +SA <19>_564,2020-09-01T00:00:00Z,SA <19>,6400,0 +SA <20>_565,2020-09-01T00:00:00Z,SA <20>,16000,0 +SB <1>_566,2020-09-01T00:00:00Z,SB <1>,5333.33,0 +SB <2>_567,2020-09-01T00:00:00Z,SB <2>,3200,0 +SB <3>_568,2020-09-01T00:00:00Z,SB <3>,10666.67,0 +SB <4>_569,2020-09-01T00:00:00Z,SB <4>,106666.67,0 +SB <5>_570,2020-09-01T00:00:00Z,SB <5>,53333.33,0 +SB <6>_571,2020-09-01T00:00:00Z,SB <6>,5333.33,0 +SB <7>_572,2020-09-01T00:00:00Z,SB <7>,2890.67,0 +SB <8>_573,2020-09-01T00:00:00Z,SB <8>,97902.93,0 +SB <9>_574,2020-09-01T00:00:00Z,SB <9>,32030.72,0 +SB <10>_575,2020-09-01T00:00:00Z,SB <10>,105450.6,0 +SB <11>_576,2020-09-01T00:00:00Z,SB <11>,28844.46,0 +SB <12>_577,2020-09-01T00:00:00Z,SB <12>,8267.36,0 +SB <13>_578,2020-09-01T00:00:00Z,SB <13>,28288.88,0 +SB <14>_579,2020-09-01T00:00:00Z,SB <14>,16000,0 +SB <15>_580,2020-09-01T00:00:00Z,SB <15>,10666.67,0 +SB <16>_581,2020-09-01T00:00:00Z,SB <16>,26666.67,0 +SB <17>_582,2020-09-01T00:00:00Z,SB <17>,21333.33,0 +SB <18>_583,2020-09-01T00:00:00Z,SB <18>,26666.67,0 +SB <19>_584,2020-09-01T00:00:00Z,SB <19>,53333.33,0 +SB <20>_585,2020-09-01T00:00:00Z,SB <20>,30880,0 +SB <21>_586,2020-09-01T00:00:00Z,SB <21>,10491.95,0 +SB <22>_587,2020-09-01T00:00:00Z,SB <22>,10666.67,0 +SB <23>_588,2020-09-01T00:00:00Z,SB <23>,2666.67,0 +SB <24>_589,2020-09-01T00:00:00Z,SB <24>,2180.66,0 +SB <25>_590,2020-09-01T00:00:00Z,SB <25>,10666.67,0 +SB <26>_591,2020-09-01T00:00:00Z,SB <26>,16832,0 +SB <27>_592,2020-09-01T00:00:00Z,SB <27>,108666.67,0 +SB <28>_593,2020-09-01T00:00:00Z,SB <28>,4800,0 +SB <29>_594,2020-09-01T00:00:00Z,SB <29>,53333.33,0 +SB <30>_595,2020-09-01T00:00:00Z,SB <30>,1066.67,0 +SB <31>_596,2020-09-01T00:00:00Z,SB <31>,16000,0 +SB <32>_597,2020-09-01T00:00:00Z,SB <32>,10666.67,0 +SB <33>_598,2020-09-01T00:00:00Z,SB <33>,10666.67,0 +SB <34>_599,2020-09-01T00:00:00Z,SB <34>,10666.67,0 +SB <35>_600,2020-09-01T00:00:00Z,SB <35>,50613.72,0 +SB <36>_601,2020-09-01T00:00:00Z,SB <36>,5642.99,0 +SB <37>_602,2020-09-01T00:00:00Z,SB <37>,2666.67,0 +SB <38>_603,2020-09-01T00:00:00Z,SB <38>,16000,0 +SB <39>_604,2020-09-01T00:00:00Z,SB <39>,8533.33,0 +SB <40>_605,2020-09-01T00:00:00Z,SB <40>,53760,0 +SB <41>_606,2020-09-01T00:00:00Z,SB <41>,48000,0 +SB <42>_607,2020-09-01T00:00:00Z,SB <42>,53333.33,0 +SB <43>_608,2020-09-01T00:00:00Z,SB <43>,26666.67,0 +SB <44>_609,2020-09-01T00:00:00Z,SB <44>,74666.67,0 +SB <45>_610,2020-09-01T00:00:00Z,SB <45>,44640,0 +SB <46>_611,2020-09-01T00:00:00Z,SB <46>,45595.9,0 +CS1_C2_612,2020-09-01T00:00:00Z,CS1_C2,666667,2 +SA <1>_613,2020-10-01T00:00:00Z,SA <1>,16000,0 +SA <2>_614,2020-10-01T00:00:00Z,SA <2>,160000,0 +SA <3>_615,2020-10-01T00:00:00Z,SA <3>,800,0 +SA <4>_616,2020-10-01T00:00:00Z,SA <4>,8000,0 +SA <5>_617,2020-10-01T00:00:00Z,SA <5>,16000,0 +SA <6>_618,2020-10-01T00:00:00Z,SA <6>,4000,0 +SA <7>_619,2020-10-01T00:00:00Z,SA <7>,3200,0 +SA <8>_620,2020-10-01T00:00:00Z,SA <8>,4000,0 +SA <9>_621,2020-10-01T00:00:00Z,SA <9>,8000,0 +SA <10>_622,2020-10-01T00:00:00Z,SA <10>,4000,0 +SA <11>_623,2020-10-01T00:00:00Z,SA <11>,4000,0 +SA <12>_624,2020-10-01T00:00:00Z,SA <12>,4000,0 +SA <13>_625,2020-10-01T00:00:00Z,SA <13>,80000,0 +SA <14>_626,2020-10-01T00:00:00Z,SA <14>,8000,0 +SA <15> _627,2020-10-01T00:00:00Z,SA <15> ,1600,0 +SA <16>_628,2020-10-01T00:00:00Z,SA <16>,4000,0 +SA <17>_629,2020-10-01T00:00:00Z,SA <17>,4000,0 +SA <18>_630,2020-10-01T00:00:00Z,SA <18>,8000,0 +SA <19>_631,2020-10-01T00:00:00Z,SA <19>,6400,0 +SA <20>_632,2020-10-01T00:00:00Z,SA <20>,16000,0 +SB <1>_633,2020-10-01T00:00:00Z,SB <1>,5333.33,0 +SB <2>_634,2020-10-01T00:00:00Z,SB <2>,3200,0 +SB <3>_635,2020-10-01T00:00:00Z,SB <3>,10666.67,0 +SB <4>_636,2020-10-01T00:00:00Z,SB <4>,106666.67,0 +SB <5>_637,2020-10-01T00:00:00Z,SB <5>,53333.33,0 +SB <6>_638,2020-10-01T00:00:00Z,SB <6>,5333.33,0 +SB <7>_639,2020-10-01T00:00:00Z,SB <7>,2890.67,0 +SB <8>_640,2020-10-01T00:00:00Z,SB <8>,97902.93,0 +SB <9>_641,2020-10-01T00:00:00Z,SB <9>,32030.72,0 +SB <10>_642,2020-10-01T00:00:00Z,SB <10>,105450.6,0 +SB <11>_643,2020-10-01T00:00:00Z,SB <11>,28844.46,0 +SB <12>_644,2020-10-01T00:00:00Z,SB <12>,8267.36,0 +SB <13>_645,2020-10-01T00:00:00Z,SB <13>,28288.88,0 +SB <14>_646,2020-10-01T00:00:00Z,SB <14>,16000,0 +SB <15>_647,2020-10-01T00:00:00Z,SB <15>,10666.67,0 +SB <16>_648,2020-10-01T00:00:00Z,SB <16>,26666.67,0 +SB <17>_649,2020-10-01T00:00:00Z,SB <17>,21333.33,0 +SB <18>_650,2020-10-01T00:00:00Z,SB <18>,26666.67,0 +SB <19>_651,2020-10-01T00:00:00Z,SB <19>,53333.33,0 +SB <20>_652,2020-10-01T00:00:00Z,SB <20>,30880,0 +SB <21>_653,2020-10-01T00:00:00Z,SB <21>,10491.95,0 +SB <22>_654,2020-10-01T00:00:00Z,SB <22>,10666.67,0 +SB <23>_655,2020-10-01T00:00:00Z,SB <23>,2666.67,0 +SB <24>_656,2020-10-01T00:00:00Z,SB <24>,2180.66,0 +SB <25>_657,2020-10-01T00:00:00Z,SB <25>,10666.67,0 +SB <26>_658,2020-10-01T00:00:00Z,SB <26>,16832,0 +SB <27>_659,2020-10-01T00:00:00Z,SB <27>,108666.67,0 +SB <28>_660,2020-10-01T00:00:00Z,SB <28>,4800,0 +SB <29>_661,2020-10-01T00:00:00Z,SB <29>,53333.33,0 +SB <30>_662,2020-10-01T00:00:00Z,SB <30>,1066.67,0 +SB <31>_663,2020-10-01T00:00:00Z,SB <31>,16000,0 +SB <32>_664,2020-10-01T00:00:00Z,SB <32>,10666.67,0 +SB <33>_665,2020-10-01T00:00:00Z,SB <33>,10666.67,0 +SB <34>_666,2020-10-01T00:00:00Z,SB <34>,10666.67,0 +SB <35>_667,2020-10-01T00:00:00Z,SB <35>,50613.72,0 +SB <36>_668,2020-10-01T00:00:00Z,SB <36>,5642.99,0 +SB <37>_669,2020-10-01T00:00:00Z,SB <37>,2666.67,0 +SB <38>_670,2020-10-01T00:00:00Z,SB <38>,16000,0 +SB <39>_671,2020-10-01T00:00:00Z,SB <39>,8533.33,0 +SB <40>_672,2020-10-01T00:00:00Z,SB <40>,53760,0 +SB <41>_673,2020-10-01T00:00:00Z,SB <41>,48000,0 +SB <42>_674,2020-10-01T00:00:00Z,SB <42>,53333.33,0 +SB <43>_675,2020-10-01T00:00:00Z,SB <43>,26666.67,0 +SB <44>_676,2020-10-01T00:00:00Z,SB <44>,74666.67,0 +SB <45>_677,2020-10-01T00:00:00Z,SB <45>,44640,0 +SB <46>_678,2020-10-01T00:00:00Z,SB <46>,45595.9,0 +CS1_C2_679,2020-10-01T00:00:00Z,CS1_C2,666667,2 +SA <1>_680,2020-11-01T00:00:00Z,SA <1>,16000,0 +SA <2>_681,2020-11-01T00:00:00Z,SA <2>,160000,0 +SA <3>_682,2020-11-01T00:00:00Z,SA <3>,800,0 +SA <4>_683,2020-11-01T00:00:00Z,SA <4>,8000,0 +SA <5>_684,2020-11-01T00:00:00Z,SA <5>,16000,0 +SA <6>_685,2020-11-01T00:00:00Z,SA <6>,4000,0 +SA <7>_686,2020-11-01T00:00:00Z,SA <7>,3200,0 +SA <8>_687,2020-11-01T00:00:00Z,SA <8>,4000,0 +SA <9>_688,2020-11-01T00:00:00Z,SA <9>,8000,0 +SA <10>_689,2020-11-01T00:00:00Z,SA <10>,4000,0 +SA <11>_690,2020-11-01T00:00:00Z,SA <11>,4000,0 +SA <12>_691,2020-11-01T00:00:00Z,SA <12>,4000,0 +SA <13>_692,2020-11-01T00:00:00Z,SA <13>,80000,0 +SA <14>_693,2020-11-01T00:00:00Z,SA <14>,8000,0 +SA <15> _694,2020-11-01T00:00:00Z,SA <15> ,1600,0 +SA <16>_695,2020-11-01T00:00:00Z,SA <16>,4000,0 +SA <17>_696,2020-11-01T00:00:00Z,SA <17>,4000,0 +SA <18>_697,2020-11-01T00:00:00Z,SA <18>,8000,0 +SA <19>_698,2020-11-01T00:00:00Z,SA <19>,6400,0 +SA <20>_699,2020-11-01T00:00:00Z,SA <20>,16000,0 +SB <1>_700,2020-11-01T00:00:00Z,SB <1>,5333.33,0 +SB <2>_701,2020-11-01T00:00:00Z,SB <2>,3200,0 +SB <3>_702,2020-11-01T00:00:00Z,SB <3>,10666.67,0 +SB <4>_703,2020-11-01T00:00:00Z,SB <4>,106666.67,0 +SB <5>_704,2020-11-01T00:00:00Z,SB <5>,53333.33,0 +SB <6>_705,2020-11-01T00:00:00Z,SB <6>,5333.33,0 +SB <7>_706,2020-11-01T00:00:00Z,SB <7>,2890.67,0 +SB <8>_707,2020-11-01T00:00:00Z,SB <8>,97902.93,0 +SB <9>_708,2020-11-01T00:00:00Z,SB <9>,32030.72,0 +SB <10>_709,2020-11-01T00:00:00Z,SB <10>,105450.6,0 +SB <11>_710,2020-11-01T00:00:00Z,SB <11>,28844.46,0 +SB <12>_711,2020-11-01T00:00:00Z,SB <12>,8267.36,0 +SB <13>_712,2020-11-01T00:00:00Z,SB <13>,28288.88,0 +SB <14>_713,2020-11-01T00:00:00Z,SB <14>,16000,0 +SB <15>_714,2020-11-01T00:00:00Z,SB <15>,10666.67,0 +SB <16>_715,2020-11-01T00:00:00Z,SB <16>,26666.67,0 +SB <17>_716,2020-11-01T00:00:00Z,SB <17>,21333.33,0 +SB <18>_717,2020-11-01T00:00:00Z,SB <18>,26666.67,0 +SB <19>_718,2020-11-01T00:00:00Z,SB <19>,53333.33,0 +SB <20>_719,2020-11-01T00:00:00Z,SB <20>,30880,0 +SB <21>_720,2020-11-01T00:00:00Z,SB <21>,10491.95,0 +SB <22>_721,2020-11-01T00:00:00Z,SB <22>,10666.67,0 +SB <23>_722,2020-11-01T00:00:00Z,SB <23>,2666.67,0 +SB <24>_723,2020-11-01T00:00:00Z,SB <24>,2180.66,0 +SB <25>_724,2020-11-01T00:00:00Z,SB <25>,10666.67,0 +SB <26>_725,2020-11-01T00:00:00Z,SB <26>,16832,0 +SB <27>_726,2020-11-01T00:00:00Z,SB <27>,108666.67,0 +SB <28>_727,2020-11-01T00:00:00Z,SB <28>,4800,0 +SB <29>_728,2020-11-01T00:00:00Z,SB <29>,53333.33,0 +SB <30>_729,2020-11-01T00:00:00Z,SB <30>,1066.67,0 +SB <31>_730,2020-11-01T00:00:00Z,SB <31>,16000,0 +SB <32>_731,2020-11-01T00:00:00Z,SB <32>,10666.67,0 +SB <33>_732,2020-11-01T00:00:00Z,SB <33>,10666.67,0 +SB <34>_733,2020-11-01T00:00:00Z,SB <34>,10666.67,0 +SB <35>_734,2020-11-01T00:00:00Z,SB <35>,50613.72,0 +SB <36>_735,2020-11-01T00:00:00Z,SB <36>,5642.99,0 +SB <37>_736,2020-11-01T00:00:00Z,SB <37>,2666.67,0 +SB <38>_737,2020-11-01T00:00:00Z,SB <38>,16000,0 +SB <39>_738,2020-11-01T00:00:00Z,SB <39>,8533.33,0 +SB <40>_739,2020-11-01T00:00:00Z,SB <40>,53760,0 +SB <41>_740,2020-11-01T00:00:00Z,SB <41>,48000,0 +SB <42>_741,2020-11-01T00:00:00Z,SB <42>,53333.33,0 +SB <43>_742,2020-11-01T00:00:00Z,SB <43>,26666.67,0 +SB <44>_743,2020-11-01T00:00:00Z,SB <44>,74666.67,0 +SB <45>_744,2020-11-01T00:00:00Z,SB <45>,44640,0 +SB <46>_745,2020-11-01T00:00:00Z,SB <46>,45595.9,0 +Coinlist Global_746,2020-11-01T00:00:00Z,Coinlist Global,20000000,0 +CS1_C2_747,2020-11-01T00:00:00Z,CS1_C2,666667,2 +SA <1>_748,2020-12-01T00:00:00Z,SA <1>,24000,0 +SA <2>_749,2020-12-01T00:00:00Z,SA <2>,240000,0 +SA <3>_750,2020-12-01T00:00:00Z,SA <3>,1200,0 +SA <4>_751,2020-12-01T00:00:00Z,SA <4>,12000,0 +SA <5>_752,2020-12-01T00:00:00Z,SA <5>,24000,0 +SA <6>_753,2020-12-01T00:00:00Z,SA <6>,6000,0 +SA <7>_754,2020-12-01T00:00:00Z,SA <7>,4800,0 +SA <8>_755,2020-12-01T00:00:00Z,SA <8>,6000,0 +SA <9>_756,2020-12-01T00:00:00Z,SA <9>,12000,0 +SA <10>_757,2020-12-01T00:00:00Z,SA <10>,6000,0 +SA <11>_758,2020-12-01T00:00:00Z,SA <11>,6000,0 +SA <12>_759,2020-12-01T00:00:00Z,SA <12>,6000,0 +SA <13>_760,2020-12-01T00:00:00Z,SA <13>,120000,0 +SA <14>_761,2020-12-01T00:00:00Z,SA <14>,12000,0 +SA <15> _762,2020-12-01T00:00:00Z,SA <15> ,2400,0 +SA <16>_763,2020-12-01T00:00:00Z,SA <16>,6000,0 +SA <17>_764,2020-12-01T00:00:00Z,SA <17>,6000,0 +SA <18>_765,2020-12-01T00:00:00Z,SA <18>,12000,0 +SA <19>_766,2020-12-01T00:00:00Z,SA <19>,9600,0 +SA <20>_767,2020-12-01T00:00:00Z,SA <20>,24000,0 +SB <1>_768,2020-12-01T00:00:00Z,SB <1>,8000,0 +SB <2>_769,2020-12-01T00:00:00Z,SB <2>,4800,0 +SB <3>_770,2020-12-01T00:00:00Z,SB <3>,16000,0 +SB <4>_771,2020-12-01T00:00:00Z,SB <4>,160000,0 +SB <5>_772,2020-12-01T00:00:00Z,SB <5>,80000,0 +SB <6>_773,2020-12-01T00:00:00Z,SB <6>,8000,0 +SB <7>_774,2020-12-01T00:00:00Z,SB <7>,4336,0 +SB <8>_775,2020-12-01T00:00:00Z,SB <8>,146854.4,0 +SB <9>_776,2020-12-01T00:00:00Z,SB <9>,48046.08,0 +SB <10>_777,2020-12-01T00:00:00Z,SB <10>,158175.9,0 +SB <11>_778,2020-12-01T00:00:00Z,SB <11>,43266.69,0 +SB <12>_779,2020-12-01T00:00:00Z,SB <12>,12401.04,0 +SB <13>_780,2020-12-01T00:00:00Z,SB <13>,42433.32,0 +SB <14>_781,2020-12-01T00:00:00Z,SB <14>,24000,0 +SB <15>_782,2020-12-01T00:00:00Z,SB <15>,16000,0 +SB <16>_783,2020-12-01T00:00:00Z,SB <16>,40000,0 +SB <17>_784,2020-12-01T00:00:00Z,SB <17>,32000,0 +SB <18>_785,2020-12-01T00:00:00Z,SB <18>,40000,0 +SB <19>_786,2020-12-01T00:00:00Z,SB <19>,80000,0 +SB <20>_787,2020-12-01T00:00:00Z,SB <20>,46320,0 +SB <21>_788,2020-12-01T00:00:00Z,SB <21>,15737.92,0 +SB <22>_789,2020-12-01T00:00:00Z,SB <22>,16000,0 +SB <23>_790,2020-12-01T00:00:00Z,SB <23>,4000,0 +SB <24>_791,2020-12-01T00:00:00Z,SB <24>,3270.98,0 +SB <25>_792,2020-12-01T00:00:00Z,SB <25>,16000,0 +SB <26>_793,2020-12-01T00:00:00Z,SB <26>,25248,0 +SB <27>_794,2020-12-01T00:00:00Z,SB <27>,163000,0 +SB <28>_795,2020-12-01T00:00:00Z,SB <28>,7200,0 +SB <29>_796,2020-12-01T00:00:00Z,SB <29>,80000,0 +SB <30>_797,2020-12-01T00:00:00Z,SB <30>,1600,0 +SB <31>_798,2020-12-01T00:00:00Z,SB <31>,24000,0 +SB <32>_799,2020-12-01T00:00:00Z,SB <32>,16000,0 +SB <33>_800,2020-12-01T00:00:00Z,SB <33>,16000,0 +SB <34>_801,2020-12-01T00:00:00Z,SB <34>,16000,0 +SB <35>_802,2020-12-01T00:00:00Z,SB <35>,75920.58,0 +SB <36>_803,2020-12-01T00:00:00Z,SB <36>,8464.48,0 +SB <37>_804,2020-12-01T00:00:00Z,SB <37>,4000,0 +SB <38>_805,2020-12-01T00:00:00Z,SB <38>,24000,0 +SB <39>_806,2020-12-01T00:00:00Z,SB <39>,12800,0 +SB <40>_807,2020-12-01T00:00:00Z,SB <40>,80640,0 +SB <41>_808,2020-12-01T00:00:00Z,SB <41>,72000,0 +SB <42>_809,2020-12-01T00:00:00Z,SB <42>,80000,0 +SB <43>_810,2020-12-01T00:00:00Z,SB <43>,40000,0 +SB <44>_811,2020-12-01T00:00:00Z,SB <44>,112000,0 +SB <45>_812,2020-12-01T00:00:00Z,SB <45>,66960,0 +SB <46>_813,2020-12-01T00:00:00Z,SB <46>,68393.85,0 +CS1_C2_814,2020-12-01T00:00:00Z,CS1_C2,666667,2 +PS_C0_815,2021-01-01T00:00:00Z,PS_C0,400000,0 +PS_C1_816,2021-01-01T00:00:00Z,PS_C1,400000,1 +PS_C2_817,2021-01-01T00:00:00Z,PS_C2,400000,2 +PS_C3_818,2021-01-01T00:00:00Z,PS_C3,400000,3 +PS_C4_819,2021-01-01T00:00:00Z,PS_C4,400000,4 +PS_C5_820,2021-01-01T00:00:00Z,PS_C5,400000,5 +PS_C6_821,2021-01-01T00:00:00Z,PS_C6,400000,6 +PS_C7_822,2021-01-01T00:00:00Z,PS_C7,400000,7 +PS_C8_823,2021-01-01T00:00:00Z,PS_C8,400000,8 +PS_C9_824,2021-01-01T00:00:00Z,PS_C9,184000,9 +EB_C9_825,2021-01-01T00:00:00Z,EB_C9,216000,9 +CS1_C2_826,2021-01-01T00:00:00Z,CS1_C2,666667,2 +ST_C1_827,2021-01-01T00:00:00Z,ST_C1,8333333,1 +PS_C0_828,2021-02-01T00:00:00Z,PS_C0,400000,0 +PS_C1_829,2021-02-01T00:00:00Z,PS_C1,400000,1 +PS_C2_830,2021-02-01T00:00:00Z,PS_C2,400000,2 +PS_C3_831,2021-02-01T00:00:00Z,PS_C3,400000,3 +PS_C4_832,2021-02-01T00:00:00Z,PS_C4,400000,4 +PS_C5_833,2021-02-01T00:00:00Z,PS_C5,400000,5 +PS_C6_834,2021-02-01T00:00:00Z,PS_C6,400000,6 +PS_C7_835,2021-02-01T00:00:00Z,PS_C7,400000,7 +PS_C8_836,2021-02-01T00:00:00Z,PS_C8,400000,8 +PS_C9_837,2021-02-01T00:00:00Z,PS_C9,184000,9 +EB_C9_838,2021-02-01T00:00:00Z,EB_C9,216000,9 +CS1_C2_839,2021-02-01T00:00:00Z,CS1_C2,666667,2 +PS_C0_840,2021-03-01T00:00:00Z,PS_C0,400000,0 +PS_C1_841,2021-03-01T00:00:00Z,PS_C1,400000,1 +PS_C2_842,2021-03-01T00:00:00Z,PS_C2,400000,2 +PS_C3_843,2021-03-01T00:00:00Z,PS_C3,400000,3 +PS_C4_844,2021-03-01T00:00:00Z,PS_C4,400000,4 +PS_C5_845,2021-03-01T00:00:00Z,PS_C5,400000,5 +PS_C6_846,2021-03-01T00:00:00Z,PS_C6,400000,6 +PS_C7_847,2021-03-01T00:00:00Z,PS_C7,400000,7 +PS_C8_848,2021-03-01T00:00:00Z,PS_C8,400000,8 +PS_C9_849,2021-03-01T00:00:00Z,PS_C9,184000,9 +EB_C9_850,2021-03-01T00:00:00Z,EB_C9,216000,9 +CS1_C2_851,2021-03-01T00:00:00Z,CS1_C2,666667,2 +PS_C0_852,2021-04-01T00:00:00Z,PS_C0,400000,0 +PS_C1_853,2021-04-01T00:00:00Z,PS_C1,400000,1 +PS_C2_854,2021-04-01T00:00:00Z,PS_C2,400000,2 +PS_C3_855,2021-04-01T00:00:00Z,PS_C3,400000,3 +PS_C4_856,2021-04-01T00:00:00Z,PS_C4,400000,4 +PS_C5_857,2021-04-01T00:00:00Z,PS_C5,400000,5 +PS_C6_858,2021-04-01T00:00:00Z,PS_C6,400000,6 +PS_C7_859,2021-04-01T00:00:00Z,PS_C7,400000,7 +PS_C8_860,2021-04-01T00:00:00Z,PS_C8,400000,8 +PS_C9_861,2021-04-01T00:00:00Z,PS_C9,184000,9 +EB_C9_862,2021-04-01T00:00:00Z,EB_C9,216000,9 +CS1_C2_863,2021-04-01T00:00:00Z,CS1_C2,666667,2 +ST_C1_864,2021-04-01T00:00:00Z,ST_C1,1666667,1 +PS_C0_865,2021-05-01T00:00:00Z,PS_C0,400000,0 +PS_C1_866,2021-05-01T00:00:00Z,PS_C1,400000,1 +PS_C2_867,2021-05-01T00:00:00Z,PS_C2,400000,2 +PS_C3_868,2021-05-01T00:00:00Z,PS_C3,400000,3 +PS_C4_869,2021-05-01T00:00:00Z,PS_C4,400000,4 +PS_C5_870,2021-05-01T00:00:00Z,PS_C5,400000,5 +PS_C6_871,2021-05-01T00:00:00Z,PS_C6,400000,6 +PS_C7_872,2021-05-01T00:00:00Z,PS_C7,400000,7 +PS_C8_873,2021-05-01T00:00:00Z,PS_C8,400000,8 +PS_C9_874,2021-05-01T00:00:00Z,PS_C9,184000,9 +EB_C9_875,2021-05-01T00:00:00Z,EB_C9,216000,9 +PS_C0_876,2021-06-01T00:00:00Z,PS_C0,400000,0 +PS_C1_877,2021-06-01T00:00:00Z,PS_C1,400000,1 +PS_C2_878,2021-06-01T00:00:00Z,PS_C2,400000,2 +PS_C3_879,2021-06-01T00:00:00Z,PS_C3,400000,3 +PS_C4_880,2021-06-01T00:00:00Z,PS_C4,400000,4 +PS_C5_881,2021-06-01T00:00:00Z,PS_C5,400000,5 +PS_C6_882,2021-06-01T00:00:00Z,PS_C6,400000,6 +PS_C7_883,2021-06-01T00:00:00Z,PS_C7,400000,7 +PS_C8_884,2021-06-01T00:00:00Z,PS_C8,400000,8 +PS_C9_885,2021-06-01T00:00:00Z,PS_C9,184000,9 +EB_C9_886,2021-06-01T00:00:00Z,EB_C9,216000,9 +PS_C0_887,2021-07-01T00:00:00Z,PS_C0,400000,0 +PS_C1_888,2021-07-01T00:00:00Z,PS_C1,400000,1 +PS_C2_889,2021-07-01T00:00:00Z,PS_C2,400000,2 +PS_C3_890,2021-07-01T00:00:00Z,PS_C3,400000,3 +PS_C4_891,2021-07-01T00:00:00Z,PS_C4,400000,4 +PS_C5_892,2021-07-01T00:00:00Z,PS_C5,400000,5 +PS_C6_893,2021-07-01T00:00:00Z,PS_C6,400000,6 +PS_C7_894,2021-07-01T00:00:00Z,PS_C7,400000,7 +PS_C8_895,2021-07-01T00:00:00Z,PS_C8,400000,8 +PS_C9_896,2021-07-01T00:00:00Z,PS_C9,184000,9 +EB_C9_897,2021-07-01T00:00:00Z,EB_C9,216000,9 +ST_C1_898,2021-07-01T00:00:00Z,ST_C1,1666667,1 +PS_C0_899,2021-08-01T00:00:00Z,PS_C0,400000,0 +PS_C1_900,2021-08-01T00:00:00Z,PS_C1,400000,1 +PS_C2_901,2021-08-01T00:00:00Z,PS_C2,400000,2 +PS_C3_902,2021-08-01T00:00:00Z,PS_C3,400000,3 +PS_C4_903,2021-08-01T00:00:00Z,PS_C4,400000,4 +PS_C5_904,2021-08-01T00:00:00Z,PS_C5,400000,5 +PS_C6_905,2021-08-01T00:00:00Z,PS_C6,400000,6 +PS_C7_906,2021-08-01T00:00:00Z,PS_C7,400000,7 +PS_C8_907,2021-08-01T00:00:00Z,PS_C8,400000,8 +PS_C9_908,2021-08-01T00:00:00Z,PS_C9,184000,9 +EB_C9_909,2021-08-01T00:00:00Z,EB_C9,216000,9 +PS_C0_910,2021-09-01T00:00:00Z,PS_C0,400000,0 +PS_C1_911,2021-09-01T00:00:00Z,PS_C1,400000,1 +PS_C2_912,2021-09-01T00:00:00Z,PS_C2,400000,2 +PS_C3_913,2021-09-01T00:00:00Z,PS_C3,400000,3 +PS_C4_914,2021-09-01T00:00:00Z,PS_C4,400000,4 +PS_C5_915,2021-09-01T00:00:00Z,PS_C5,400000,5 +PS_C6_916,2021-09-01T00:00:00Z,PS_C6,400000,6 +PS_C7_917,2021-09-01T00:00:00Z,PS_C7,400000,7 +PS_C8_918,2021-09-01T00:00:00Z,PS_C8,400000,8 +PS_C9_919,2021-09-01T00:00:00Z,PS_C9,184000,9 +EB_C9_920,2021-09-01T00:00:00Z,EB_C9,216000,9 +PS_C0_921,2021-10-01T00:00:00Z,PS_C0,400000,0 +PS_C1_922,2021-10-01T00:00:00Z,PS_C1,400000,1 +PS_C2_923,2021-10-01T00:00:00Z,PS_C2,400000,2 +PS_C3_924,2021-10-01T00:00:00Z,PS_C3,400000,3 +PS_C4_925,2021-10-01T00:00:00Z,PS_C4,400000,4 +PS_C5_926,2021-10-01T00:00:00Z,PS_C5,400000,5 +PS_C6_927,2021-10-01T00:00:00Z,PS_C6,400000,6 +PS_C7_928,2021-10-01T00:00:00Z,PS_C7,400000,7 +PS_C8_929,2021-10-01T00:00:00Z,PS_C8,400000,8 +PS_C9_930,2021-10-01T00:00:00Z,PS_C9,184000,9 +EB_C9_931,2021-10-01T00:00:00Z,EB_C9,216000,9 +ST_C1_932,2021-10-01T00:00:00Z,ST_C1,1666667,1 +PS_C0_933,2021-11-01T00:00:00Z,PS_C0,400000,0 +PS_C1_934,2021-11-01T00:00:00Z,PS_C1,400000,1 +PS_C2_935,2021-11-01T00:00:00Z,PS_C2,400000,2 +PS_C3_936,2021-11-01T00:00:00Z,PS_C3,400000,3 +PS_C4_937,2021-11-01T00:00:00Z,PS_C4,400000,4 +PS_C5_938,2021-11-01T00:00:00Z,PS_C5,400000,5 +PS_C6_939,2021-11-01T00:00:00Z,PS_C6,400000,6 +PS_C7_940,2021-11-01T00:00:00Z,PS_C7,400000,7 +PS_C8_941,2021-11-01T00:00:00Z,PS_C8,400000,8 +PS_C9_942,2021-11-01T00:00:00Z,PS_C9,184000,9 +EB_C9_943,2021-11-01T00:00:00Z,EB_C9,216000,9 +PS_C0_944,2021-12-01T00:00:00Z,PS_C0,400000,0 +PS_C1_945,2021-12-01T00:00:00Z,PS_C1,400000,1 +PS_C2_946,2021-12-01T00:00:00Z,PS_C2,400000,2 +PS_C3_947,2021-12-01T00:00:00Z,PS_C3,400000,3 +PS_C4_948,2021-12-01T00:00:00Z,PS_C4,400000,4 +PS_C5_949,2021-12-01T00:00:00Z,PS_C5,400000,5 +PS_C6_950,2021-12-01T00:00:00Z,PS_C6,400000,6 +PS_C7_951,2021-12-01T00:00:00Z,PS_C7,400000,7 +PS_C8_952,2021-12-01T00:00:00Z,PS_C8,400000,8 +PS_C9_953,2021-12-01T00:00:00Z,PS_C9,176000,9 +EB_C9_954,2021-12-01T00:00:00Z,EB_C9,224000,9 +PS_C0_955,2022-01-01T00:00:00Z,PS_C0,400000,0 +PS_C1_956,2022-01-01T00:00:00Z,PS_C1,400000,1 +PS_C2_957,2022-01-01T00:00:00Z,PS_C2,400000,2 +PS_C3_958,2022-01-01T00:00:00Z,PS_C3,400000,3 +PS_C4_959,2022-01-01T00:00:00Z,PS_C4,400000,4 +PS_C5_960,2022-01-01T00:00:00Z,PS_C5,400000,5 +PS_C6_961,2022-01-01T00:00:00Z,PS_C6,400000,6 +PS_C7_962,2022-01-01T00:00:00Z,PS_C7,400000,7 +PS_C8_963,2022-01-01T00:00:00Z,PS_C8,400000,8 +PS_C9_964,2022-01-01T00:00:00Z,PS_C9,400000,9 +ST_C1_965,2022-01-01T00:00:00Z,ST_C1,1666667,1 +PS_C0_966,2022-02-01T00:00:00Z,PS_C0,400000,0 +PS_C1_967,2022-02-01T00:00:00Z,PS_C1,400000,1 +PS_C2_968,2022-02-01T00:00:00Z,PS_C2,400000,2 +PS_C3_969,2022-02-01T00:00:00Z,PS_C3,400000,3 +PS_C4_970,2022-02-01T00:00:00Z,PS_C4,400000,4 +PS_C5_971,2022-02-01T00:00:00Z,PS_C5,400000,5 +PS_C6_972,2022-02-01T00:00:00Z,PS_C6,400000,6 +PS_C7_973,2022-02-01T00:00:00Z,PS_C7,400000,7 +PS_C8_974,2022-02-01T00:00:00Z,PS_C8,400000,8 +PS_C9_975,2022-02-01T00:00:00Z,PS_C9,400000,9 +PS_C0_976,2022-03-01T00:00:00Z,PS_C0,400000,0 +PS_C1_977,2022-03-01T00:00:00Z,PS_C1,400000,1 +PS_C2_978,2022-03-01T00:00:00Z,PS_C2,400000,2 +PS_C3_979,2022-03-01T00:00:00Z,PS_C3,400000,3 +PS_C4_980,2022-03-01T00:00:00Z,PS_C4,400000,4 +PS_C5_981,2022-03-01T00:00:00Z,PS_C5,400000,5 +PS_C6_982,2022-03-01T00:00:00Z,PS_C6,400000,6 +PS_C7_983,2022-03-01T00:00:00Z,PS_C7,400000,7 +PS_C8_984,2022-03-01T00:00:00Z,PS_C8,400000,8 +PS_C9_985,2022-03-01T00:00:00Z,PS_C9,400000,9 +PS_C0_986,2022-04-01T00:00:00Z,PS_C0,400000,0 +PS_C1_987,2022-04-01T00:00:00Z,PS_C1,400000,1 +PS_C2_988,2022-04-01T00:00:00Z,PS_C2,400000,2 +PS_C3_989,2022-04-01T00:00:00Z,PS_C3,400000,3 +PS_C4_990,2022-04-01T00:00:00Z,PS_C4,400000,4 +PS_C5_991,2022-04-01T00:00:00Z,PS_C5,400000,5 +PS_C6_992,2022-04-01T00:00:00Z,PS_C6,400000,6 +PS_C7_993,2022-04-01T00:00:00Z,PS_C7,400000,7 +PS_C8_994,2022-04-01T00:00:00Z,PS_C8,400000,8 +PS_C9_995,2022-04-01T00:00:00Z,PS_C9,400000,9 +ST_C1_996,2022-04-01T00:00:00Z,ST_C1,1666667,1 +PS_C0_997,2022-05-01T00:00:00Z,PS_C0,400000,0 +PS_C1_998,2022-05-01T00:00:00Z,PS_C1,400000,1 +PS_C2_999,2022-05-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1000,2022-05-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1001,2022-05-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1002,2022-05-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1003,2022-05-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1004,2022-05-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1005,2022-05-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1006,2022-05-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1007,2022-06-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1008,2022-06-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1009,2022-06-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1010,2022-06-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1011,2022-06-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1012,2022-06-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1013,2022-06-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1014,2022-06-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1015,2022-06-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1016,2022-06-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1017,2022-07-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1018,2022-07-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1019,2022-07-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1020,2022-07-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1021,2022-07-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1022,2022-07-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1023,2022-07-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1024,2022-07-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1025,2022-07-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1026,2022-07-01T00:00:00Z,PS_C9,400000,9 +ST_C1_1027,2022-07-01T00:00:00Z,ST_C1,1666667,1 +PS_C0_1028,2022-08-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1029,2022-08-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1030,2022-08-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1031,2022-08-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1032,2022-08-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1033,2022-08-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1034,2022-08-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1035,2022-08-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1036,2022-08-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1037,2022-08-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1038,2022-09-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1039,2022-09-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1040,2022-09-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1041,2022-09-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1042,2022-09-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1043,2022-09-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1044,2022-09-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1045,2022-09-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1046,2022-09-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1047,2022-09-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1048,2022-10-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1049,2022-10-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1050,2022-10-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1051,2022-10-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1052,2022-10-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1053,2022-10-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1054,2022-10-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1055,2022-10-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1056,2022-10-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1057,2022-10-01T00:00:00Z,PS_C9,400000,9 +ST_C1_1058,2022-10-01T00:00:00Z,ST_C1,1666667,1 +PS_C0_1059,2022-11-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1060,2022-11-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1061,2022-11-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1062,2022-11-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1063,2022-11-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1064,2022-11-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1065,2022-11-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1066,2022-11-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1067,2022-11-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1068,2022-11-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1069,2022-12-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1070,2022-12-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1071,2022-12-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1072,2022-12-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1073,2022-12-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1074,2022-12-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1075,2022-12-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1076,2022-12-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1077,2022-12-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1078,2022-12-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1079,2023-01-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1080,2023-01-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1081,2023-01-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1082,2023-01-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1083,2023-01-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1084,2023-01-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1085,2023-01-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1086,2023-01-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1087,2023-01-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1088,2023-01-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1089,2023-02-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1090,2023-02-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1091,2023-02-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1092,2023-02-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1093,2023-02-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1094,2023-02-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1095,2023-02-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1096,2023-02-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1097,2023-02-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1098,2023-02-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1099,2023-03-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1100,2023-03-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1101,2023-03-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1102,2023-03-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1103,2023-03-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1104,2023-03-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1105,2023-03-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1106,2023-03-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1107,2023-03-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1108,2023-03-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1109,2023-04-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1110,2023-04-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1111,2023-04-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1112,2023-04-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1113,2023-04-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1114,2023-04-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1115,2023-04-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1116,2023-04-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1117,2023-04-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1118,2023-04-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1119,2023-05-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1120,2023-05-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1121,2023-05-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1122,2023-05-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1123,2023-05-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1124,2023-05-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1125,2023-05-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1126,2023-05-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1127,2023-05-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1128,2023-05-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1129,2023-06-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1130,2023-06-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1131,2023-06-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1132,2023-06-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1133,2023-06-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1134,2023-06-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1135,2023-06-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1136,2023-06-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1137,2023-06-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1138,2023-06-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1139,2023-07-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1140,2023-07-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1141,2023-07-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1142,2023-07-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1143,2023-07-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1144,2023-07-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1145,2023-07-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1146,2023-07-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1147,2023-07-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1148,2023-07-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1149,2023-08-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1150,2023-08-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1151,2023-08-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1152,2023-08-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1153,2023-08-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1154,2023-08-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1155,2023-08-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1156,2023-08-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1157,2023-08-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1158,2023-08-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1159,2023-09-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1160,2023-09-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1161,2023-09-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1162,2023-09-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1163,2023-09-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1164,2023-09-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1165,2023-09-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1166,2023-09-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1167,2023-09-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1168,2023-09-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1169,2023-10-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1170,2023-10-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1171,2023-10-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1172,2023-10-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1173,2023-10-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1174,2023-10-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1175,2023-10-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1176,2023-10-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1177,2023-10-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1178,2023-10-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1179,2023-11-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1180,2023-11-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1181,2023-11-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1182,2023-11-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1183,2023-11-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1184,2023-11-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1185,2023-11-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1186,2023-11-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1187,2023-11-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1188,2023-11-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1189,2023-12-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1190,2023-12-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1191,2023-12-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1192,2023-12-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1193,2023-12-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1194,2023-12-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1195,2023-12-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1196,2023-12-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1197,2023-12-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1198,2023-12-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1199,2024-01-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1200,2024-01-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1201,2024-01-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1202,2024-01-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1203,2024-01-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1204,2024-01-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1205,2024-01-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1206,2024-01-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1207,2024-01-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1208,2024-01-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1209,2024-02-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1210,2024-02-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1211,2024-02-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1212,2024-02-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1213,2024-02-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1214,2024-02-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1215,2024-02-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1216,2024-02-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1217,2024-02-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1218,2024-02-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1219,2024-03-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1220,2024-03-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1221,2024-03-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1222,2024-03-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1223,2024-03-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1224,2024-03-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1225,2024-03-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1226,2024-03-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1227,2024-03-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1228,2024-03-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1229,2024-04-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1230,2024-04-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1231,2024-04-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1232,2024-04-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1233,2024-04-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1234,2024-04-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1235,2024-04-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1236,2024-04-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1237,2024-04-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1238,2024-04-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1239,2024-05-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1240,2024-05-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1241,2024-05-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1242,2024-05-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1243,2024-05-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1244,2024-05-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1245,2024-05-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1246,2024-05-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1247,2024-05-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1248,2024-05-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1249,2024-06-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1250,2024-06-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1251,2024-06-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1252,2024-06-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1253,2024-06-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1254,2024-06-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1255,2024-06-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1256,2024-06-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1257,2024-06-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1258,2024-06-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1259,2024-07-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1260,2024-07-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1261,2024-07-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1262,2024-07-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1263,2024-07-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1264,2024-07-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1265,2024-07-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1266,2024-07-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1267,2024-07-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1268,2024-07-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1269,2024-08-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1270,2024-08-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1271,2024-08-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1272,2024-08-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1273,2024-08-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1274,2024-08-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1275,2024-08-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1276,2024-08-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1277,2024-08-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1278,2024-08-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1279,2024-09-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1280,2024-09-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1281,2024-09-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1282,2024-09-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1283,2024-09-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1284,2024-09-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1285,2024-09-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1286,2024-09-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1287,2024-09-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1288,2024-09-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1289,2024-10-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1290,2024-10-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1291,2024-10-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1292,2024-10-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1293,2024-10-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1294,2024-10-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1295,2024-10-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1296,2024-10-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1297,2024-10-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1298,2024-10-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1299,2024-11-01T00:00:00Z,PS_C0,400000,0 +PS_C1_1300,2024-11-01T00:00:00Z,PS_C1,400000,1 +PS_C2_1301,2024-11-01T00:00:00Z,PS_C2,400000,2 +PS_C3_1302,2024-11-01T00:00:00Z,PS_C3,400000,3 +PS_C4_1303,2024-11-01T00:00:00Z,PS_C4,400000,4 +PS_C5_1304,2024-11-01T00:00:00Z,PS_C5,400000,5 +PS_C6_1305,2024-11-01T00:00:00Z,PS_C6,400000,6 +PS_C7_1306,2024-11-01T00:00:00Z,PS_C7,400000,7 +PS_C8_1307,2024-11-01T00:00:00Z,PS_C8,400000,8 +PS_C9_1308,2024-11-01T00:00:00Z,PS_C9,400000,9 +PS_C0_1309,2024-12-01T00:00:00Z,PS_C0,1200000,0 +PS_C1_1310,2024-12-01T00:00:00Z,PS_C1,1200000,1 +PS_C2_1311,2024-12-01T00:00:00Z,PS_C2,1200000,2 +PS_C3_1312,2024-12-01T00:00:00Z,PS_C3,1200000,3 +PS_C4_1313,2024-12-01T00:00:00Z,PS_C4,1200000,4 +PS_C5_1314,2024-12-01T00:00:00Z,PS_C5,1200000,5 +PS_C6_1315,2024-12-01T00:00:00Z,PS_C6,1200000,6 +PS_C7_1316,2024-12-01T00:00:00Z,PS_C7,1200000,7 +PS_C8_1317,2024-12-01T00:00:00Z,PS_C8,1200000,8 +PS_C9_1318,2024-12-01T00:00:00Z,PS_C9,1200000,9 diff --git a/indexer/jest.config.ts b/indexer/jest.config.ts new file mode 100644 index 00000000..5f65f4b4 --- /dev/null +++ b/indexer/jest.config.ts @@ -0,0 +1,11 @@ +export default { + preset: "ts-jest", + testEnvironment: "node", + transform: { + "^.+\\.ts$": "ts-jest", + }, + extensionsToTreatAsEsm: [".ts"], + moduleNameMapper: { + "^(\\.{1,2}/.*)\\.js$": "$1", + }, +}; diff --git a/indexer/package.json b/indexer/package.json index 6f77c512..b9a4e449 100644 --- a/indexer/package.json +++ b/indexer/package.json @@ -28,8 +28,9 @@ "graphql-subscriptions": "^2.0.0", "graphql-tag": "^2.12.6", "graphql-ws": "^5.16.0", - "json-bigint": "^1.0.0", + "ip-range-check": "^0.2.0", "node-cache": "^5.1.2", + "papaparse": "^5.5.2", "pg": "^8.11.3", "pg-hstore": "^2.3.4", "postgraphile": "^4.13.0", @@ -48,6 +49,7 @@ "@types/jest": "^29.5.12", "@types/mocha": "^10.0.7", "@types/node": "^20.11.17", + "@types/papaparse": "^5.3.15", "@typescript-eslint/eslint-plugin": "^7.0.1", "@typescript-eslint/parser": "^7.0.1", "chai": "^5.1.1", @@ -72,8 +74,9 @@ "prod:streaming": "node dist/index.js --streaming", "prod:backfill": "node dist/index.js --backfill", "test": "NODE_ENV=test mocha -r ts-node/register 'tests/**/*.test.ts'", + "test:unit": "jest tests/unit/*.test.ts", "create:database": "ts-node src/index.ts --database && yarn migrate:up", "migrate:up": "dotenv -e .env npx sequelize-cli db:migrate", "migrate:down": "dotenv -e .env npx sequelize-cli db:migrate:undo" } -} \ No newline at end of file +} diff --git a/indexer/src/cache/init.ts b/indexer/src/cache/init.ts index 03d0c31a..bb9807be 100644 --- a/indexer/src/cache/init.ts +++ b/indexer/src/cache/init.ts @@ -9,7 +9,7 @@ import { HashRateAndTotalDifficulty } from "../kadena-server/repository/applicat export const MEMORY_CACHE = new NodeCache({ stdTTL: 0 }); -const CACHE_TTL = 1000 * 60 * 5; // 5 minutes +const CACHE_TTL = 1000 * 30; // 30 seconds export default async function initCache(context: ResolverContext) { const { blockRepository, networkRepository } = context; diff --git a/indexer/src/jobs/publisher-job.ts b/indexer/src/jobs/publisher-job.ts index b68f7065..04104c93 100644 --- a/indexer/src/jobs/publisher-job.ts +++ b/indexer/src/jobs/publisher-job.ts @@ -27,8 +27,10 @@ export const dispatch = async (dispatchInfo: DispatchInfo) => { const errorData = await response.json(); throw new Error(errorData.message || response.statusText); } + return true; } catch (err: unknown) { const errorData = err instanceof Error ? err.message : "Unknown error"; console.error("Dispatcher error:", errorData); + return false; } }; diff --git a/indexer/src/kadena-server/config/graphql-types.ts b/indexer/src/kadena-server/config/graphql-types.ts index 2efb236a..cb792746 100644 --- a/indexer/src/kadena-server/config/graphql-types.ts +++ b/indexer/src/kadena-server/config/graphql-types.ts @@ -23,12 +23,16 @@ export type Scalars = { Decimal: { input: any; output: any; } }; +/** A unit of information that stores a set of verified transactions. */ export type Block = Node & { __typename?: 'Block'; chainId: Scalars['BigInt']['output']; creationTime: Scalars['DateTime']['output']; - difficulty?: Maybe; + /** The difficulty of the block. */ + difficulty: Scalars['BigInt']['output']; + /** The moment the difficulty is adjusted to maintain a block validation time of 30 seconds. */ epoch: Scalars['DateTime']['output']; + /** Default page size is 20. */ events: BlockEventsConnection; flags: Scalars['Decimal']['output']; hash: Scalars['String']['output']; @@ -39,13 +43,16 @@ export type Block = Node & { nonce: Scalars['Decimal']['output']; parent?: Maybe; payloadHash: Scalars['String']['output']; + /** The proof of work hash. */ powHash: Scalars['String']['output']; target: Scalars['String']['output']; + /** Default page size is 20. */ transactions: BlockTransactionsConnection; weight: Scalars['String']['output']; }; +/** A unit of information that stores a set of verified transactions. */ export type BlockEventsArgs = { after?: InputMaybe; before?: InputMaybe; @@ -54,6 +61,7 @@ export type BlockEventsArgs = { }; +/** A unit of information that stores a set of verified transactions. */ export type BlockTransactionsArgs = { after?: InputMaybe; before?: InputMaybe; @@ -74,6 +82,7 @@ export type BlockEventsConnectionEdge = { node: Event; }; +/** The neighbor of a block. */ export type BlockNeighbor = { __typename?: 'BlockNeighbor'; chainId: Scalars['String']['output']; @@ -93,36 +102,51 @@ export type BlockTransactionsConnectionEdge = { node: Transaction; }; +/** The payload of an cont transaction. */ export type ContinuationPayload = { __typename?: 'ContinuationPayload'; + /** The environment data made available to the transaction. Formatted as raw JSON. */ data: Scalars['String']['output']; + /** A unique id when a pact (defpact) is initiated. See the "Pact execution scope and pact-id" explanation in the docs for more information. */ pactId?: Maybe; + /** The proof provided to continue the cross-chain transaction. */ proof?: Maybe; + /** Whether or not this transaction can be rolled back. */ rollback?: Maybe; + /** The step-number when this is an execution of a `defpact`, aka multi-step transaction. */ step?: Maybe; }; +/** An event emitted by the execution of a smart-contract function. */ export type Event = Node & { __typename?: 'Event'; block: Block; chainId: Scalars['BigInt']['output']; + /** The height of the block where the event was emitted. */ height: Scalars['BigInt']['output']; id: Scalars['ID']['output']; moduleName: Scalars['String']['output']; - name?: Maybe; + name: Scalars['String']['output']; + /** The order index of this event, in the case that there are multiple events in one transaction. */ orderIndex: Scalars['BigInt']['output']; + parameterText: Scalars['String']['output']; parameters?: Maybe; + /** The full eventname, containing module and eventname, e.g. coin.TRANSFER */ qualifiedName: Scalars['String']['output']; requestKey: Scalars['String']['output']; transaction?: Maybe; }; +/** The payload of an exec transaction. */ export type ExecutionPayload = { __typename?: 'ExecutionPayload'; + /** The Pact expressions executed in this transaction when it is an `exec` transaction. */ code?: Maybe; + /** The environment data made available to the transaction. Formatted as raw JSON. */ data: Scalars['String']['output']; }; +/** A fungible-specific account. */ export type FungibleAccount = Node & { __typename?: 'FungibleAccount'; accountName: Scalars['String']['output']; @@ -130,11 +154,14 @@ export type FungibleAccount = Node & { fungibleName: Scalars['String']['output']; id: Scalars['ID']['output']; totalBalance: Scalars['Decimal']['output']; + /** Default page size is 20. */ transactions: FungibleAccountTransactionsConnection; + /** Default page size is 20. */ transfers: FungibleAccountTransfersConnection; }; +/** A fungible-specific account. */ export type FungibleAccountTransactionsArgs = { after?: InputMaybe; before?: InputMaybe; @@ -143,6 +170,7 @@ export type FungibleAccountTransactionsArgs = { }; +/** A fungible-specific account. */ export type FungibleAccountTransfersArgs = { after?: InputMaybe; before?: InputMaybe; @@ -176,19 +204,23 @@ export type FungibleAccountTransfersConnectionEdge = { node: Transfer; }; +/** A fungible specific chain-account. */ export type FungibleChainAccount = Node & { __typename?: 'FungibleChainAccount'; accountName: Scalars['String']['output']; balance: Scalars['Float']['output']; chainId: Scalars['String']['output']; fungibleName: Scalars['String']['output']; - guard: Guard; + guard: IGuard; id: Scalars['ID']['output']; + /** Transactions that the current account is sender of. Default page size is 20. */ transactions: FungibleChainAccountTransactionsConnection; + /** Default page size is 20. */ transfers: FungibleChainAccountTransfersConnection; }; +/** A fungible specific chain-account. */ export type FungibleChainAccountTransactionsArgs = { after?: InputMaybe; before?: InputMaybe; @@ -197,6 +229,7 @@ export type FungibleChainAccountTransactionsArgs = { }; +/** A fungible specific chain-account. */ export type FungibleChainAccountTransfersArgs = { after?: InputMaybe; before?: InputMaybe; @@ -245,14 +278,27 @@ export type GenesisHeight = { height: Scalars['Int']['output']; }; +/** General information about the graph and chainweb-data. */ export type GraphConfiguration = { __typename?: 'GraphConfiguration'; /** The lowest block-height that is indexed in this endpoint. */ minimumBlockHeight?: Maybe; + /** The version of the graphl api. */ + version: Scalars['String']['output']; +}; + +/** A guard. This is a union of all the different types of guards that can be used in a pact. */ +export type IGuard = { + /** @deprecated deprecated, use KeysetGuard.keys */ + keys: Array; + /** @deprecated deprecated, use KeysetGuard.predicate */ + predicate: Scalars['String']['output']; + raw: Scalars['String']['output']; }; -export type Guard = { - __typename?: 'Guard'; +/** A keyset guard. */ +export type KeysetGuard = IGuard & { + __typename?: 'KeysetGuard'; keys: Array; predicate: Scalars['String']['output']; raw: Scalars['String']['output']; @@ -292,6 +338,7 @@ export type Node = { export type NonFungibleAccount = Node & { __typename?: 'NonFungibleAccount'; accountName: Scalars['String']['output']; + chainAccounts: Array; id: Scalars['ID']['output']; nonFungibleTokenBalances: Array; /** Default page size is 20. Note that custom token related transactions are not included. */ @@ -320,16 +367,19 @@ export type NonFungibleAccountTransactionsConnectionEdge = { node: Transaction; }; +/** A chain and non-fungible-specific account. */ export type NonFungibleChainAccount = Node & { __typename?: 'NonFungibleChainAccount'; accountName: Scalars['String']['output']; chainId: Scalars['String']['output']; id: Scalars['ID']['output']; nonFungibleTokenBalances: Array; + /** Default page size is 20. Note that custom token related transactions are not included. */ transactions: NonFungibleChainAccountTransactionsConnection; }; +/** A chain and non-fungible-specific account. */ export type NonFungibleChainAccountTransactionsArgs = { after?: InputMaybe; before?: InputMaybe; @@ -350,6 +400,7 @@ export type NonFungibleChainAccountTransactionsConnectionEdge = { node: Transaction; }; +/** Information related to a token. */ export type NonFungibleToken = { __typename?: 'NonFungibleToken'; precision: Scalars['Int']['output']; @@ -357,11 +408,13 @@ export type NonFungibleToken = { uri: Scalars['String']['output']; }; +/** The token identifier and its balance. */ export type NonFungibleTokenBalance = Node & { __typename?: 'NonFungibleTokenBalance'; accountName: Scalars['String']['output']; balance: Scalars['Int']['output']; chainId: Scalars['String']['output']; + guard: IGuard; id: Scalars['ID']['output']; info?: Maybe; tokenId: Scalars['String']['output']; @@ -399,32 +452,43 @@ export type PageInfo = { export type Query = { __typename?: 'Query'; + /** Retrieve a block by hash. */ block?: Maybe; + /** Retrieve blocks by chain and minimal depth. Default page size is 20. */ blocksFromDepth?: Maybe; + /** Retrieve blocks by chain and minimal height. Default page size is 20. */ blocksFromHeight: QueryBlocksFromHeightConnection; + /** Retrieve all completed blocks from a given height. Default page size is 20. */ completedBlockHeights: QueryCompletedBlockHeightsConnection; + /** + * Retrieve events by qualifiedName (e.g. `coin.TRANSFER`). Default page size is 20. + * + * The parametersFilter is a stringified JSON object that matches the [JSON object property filters](https://www.prisma.io/docs/orm/prisma-client/special-fields-and-types/working-with-json-fields#filter-on-object-property) from Prisma. + * + * An example of such a filter parameter value: `events(parametersFilter: "{\"array_starts_with\": \"k:abcdefg\"}")` + */ events: QueryEventsConnection; /** Retrieve an fungible specific account by its name and fungible, such as coin. */ fungibleAccount?: Maybe; /** Retrieve an account by public key. */ fungibleAccountsByPublicKey: Array; /** Retrieve an account by its name and fungible, such as coin, on a specific chain. */ - fungibleChainAccount: Array; + fungibleChainAccounts: Array; /** Retrieve a chain account by public key. */ fungibleChainAccountsByPublicKey: Array; /** * Estimate the gas limit for one or more transactions. Throws an error when the transaction fails or is invalid. The input accepts a JSON object and based on the parameters passed it will determine what type of format it is and return the gas limit estimation. The following types are supported: - * - * - `full-transaction`: A complete transaction object. Required parameters: `cmd`, `hash` and `sigs`. - * - `stringified-command`: A JSON stringified command. Required parameters: `cmd`. It also optionally accepts `sigs`. - * - `full-command`: A full command. Required parameters: `payload`, `meta` and `signers`. - * - `partial-command`: A partial command. Required parameters: `payload` and either `meta` or `signers`. In case `meta` is not given, but `signers` is given, you can also add `chainId` as a parameter. - * - `payload`: A just the payload of a command. Required parameters: `payload` and `chainId`. - * - `code`: The code of an execution. Required parameters: `code` and `chainId`. - * - * Every type accepts an optional parameter called `networkId` to override the default value from the environment variables. - * - * Example of the input needed for a type `code` query: `gasLimitEstimate(input: "{\"code\":\"(coin.details \\\"k:1234\\\")\",\"chainId\":\"3\"}")` + *   + * - `full-transaction`: A complete transaction object. Required parameters: `cmd`, `hash` and `sigs`. + * - `stringified-command`: A JSON stringified command. Required parameters: `cmd`. It also optionally accepts `sigs`. + * - `full-command`: A full command. Required parameters: `payload`, `meta` and `signers`. + * - `partial-command`: A partial command. Required parameters: `payload` and either `meta` or `signers`. In case `meta` is not given, but `signers` is given, you can also add `chainId` as a parameter. + * - `payload`: A just the payload of a command. Required parameters: `payload` and `chainId`. + * - `code`: The code of an execution. Required parameters: `code` and `chainId`. + *   + * Every type accepts an optional parameter called `networkId` to override the default value from the environment variables. + *   + * Example of the input needed for a type `code` query: `gasLimitEstimate(input: "{\"code\":\"(coin.details \\\"k:1234\\\")\",\"chainId\":\"3\"}")` */ gasLimitEstimate: Array; /** Get the configuration of the graph. */ @@ -441,9 +505,16 @@ export type Query = { nonFungibleChainAccount?: Maybe; /** Execute arbitrary Pact code via a local call without gas-estimation or signature-verification (e.g. (+ 1 2) or (coin.get-details )). */ pactQuery: Array; + /** Retrieve one transaction by its unique key. Throws an error if multiple transactions are found. */ transaction?: Maybe; + /** + * Retrieve transactions. Default page size is 20. + * At least one of accountName, fungibleName, blockHash, or requestKey must be provided. + */ transactions: QueryTransactionsConnection; + /** Retrieve all transactions by a given public key. */ transactionsByPublicKey: QueryTransactionsByPublicKeyConnection; + /** Retrieve transfers. Default page size is 20. */ transfers: QueryTransfersConnection; }; @@ -514,7 +585,7 @@ export type QueryFungibleAccountsByPublicKeyArgs = { }; -export type QueryFungibleChainAccountArgs = { +export type QueryFungibleChainAccountsArgs = { accountName: Scalars['String']['input']; chainIds?: InputMaybe>; fungibleName?: InputMaybe; @@ -691,21 +762,34 @@ export type QueryTransfersConnectionEdge = { node: Transfer; }; +/** A signer for a specific transaction. */ export type Signer = Node & { __typename?: 'Signer'; + /** The signer for the gas. */ address?: Maybe; clist: Array; id: Scalars['ID']['output']; orderIndex?: Maybe; pubkey: Scalars['String']['output']; + /** The signature scheme that was used to sign. */ scheme?: Maybe; }; export type Subscription = { __typename?: 'Subscription'; + /** + * Listen for events by qualifiedName (e.g. `coin.TRANSFER`). + *   + * The parametersFilter is a stringified JSON object that matches the [JSON object property filters](https://www.prisma.io/docs/orm/prisma-client/special-fields-and-types/working-with-json-fields#filter-on-object-property) from Prisma. + *   + * An example of such a filter parameter value: `events(parametersFilter: "{\"array_starts_with\": \"k:abcdefg\"}")` + */ events?: Maybe>; + /** Subscribe to new blocks. */ newBlocks?: Maybe>; + /** Subscribe to new blocks from a specific depth. */ newBlocksFromDepth?: Maybe>; + /** Listen for a transaction by request key. */ transaction?: Maybe; }; @@ -713,17 +797,18 @@ export type Subscription = { export type SubscriptionEventsArgs = { chainId?: InputMaybe; minimumDepth?: InputMaybe; + parametersFilter?: InputMaybe; qualifiedEventName: Scalars['String']['input']; }; export type SubscriptionNewBlocksArgs = { - chainIds: Array>; + chainIds?: InputMaybe>; }; export type SubscriptionNewBlocksFromDepthArgs = { - chainIds: Array; + chainIds?: InputMaybe>; minimumDepth: Scalars['Int']['input']; }; @@ -733,6 +818,7 @@ export type SubscriptionTransactionArgs = { requestKey: Scalars['String']['input']; }; +/** A transaction. */ export type Transaction = Node & { __typename?: 'Transaction'; cmd: TransactionCommand; @@ -743,12 +829,14 @@ export type Transaction = Node & { sigs: Array; }; +/** List of capabilities associated with/installed by this signer. */ export type TransactionCapability = { __typename?: 'TransactionCapability'; args: Scalars['String']['output']; name: Scalars['String']['output']; }; +/** A transaction command. */ export type TransactionCommand = { __typename?: 'TransactionCommand'; meta: TransactionMeta; @@ -759,13 +847,17 @@ export type TransactionCommand = { signers: Array; }; +/** The result of a transaction. */ export type TransactionInfo = TransactionMempoolInfo | TransactionResult; +/** The mempool information. */ export type TransactionMempoolInfo = { __typename?: 'TransactionMempoolInfo'; + /** The status of the mempool. */ status?: Maybe; }; +/** The metadata of a transaction. */ export type TransactionMeta = { __typename?: 'TransactionMeta'; chainId: Scalars['BigInt']['output']; @@ -776,24 +868,32 @@ export type TransactionMeta = { ttl: Scalars['BigInt']['output']; }; +/** The payload of a transaction. */ export type TransactionPayload = ContinuationPayload | ExecutionPayload; +/** The result of a transaction. */ export type TransactionResult = { __typename?: 'TransactionResult'; + /** The transaction result when it was successful. Formatted as raw JSON. */ badResult?: Maybe; block: Block; + /** The JSON stringified continuation in the case that it is a continuation. */ continuation?: Maybe; eventCount?: Maybe; events: TransactionResultEventsConnection; gas: Scalars['BigInt']['output']; + /** The transaction result when it was successful. Formatted as raw JSON. */ goodResult?: Maybe; - height: Scalars['BigInt']['output']; + /** Identifier to retrieve the logs for the execution of the transaction. */ logs?: Maybe; + /** @deprecated Not used. */ + metadata: Scalars['String']['output']; transactionId?: Maybe; transfers: TransactionResultTransfersConnection; }; +/** The result of a transaction. */ export type TransactionResultEventsArgs = { after?: InputMaybe; before?: InputMaybe; @@ -802,6 +902,7 @@ export type TransactionResultEventsArgs = { }; +/** The result of a transaction. */ export type TransactionResultTransfersArgs = { after?: InputMaybe; before?: InputMaybe; @@ -814,7 +915,6 @@ export type TransactionResultEventsConnection = { edges: Array>; pageInfo: PageInfo; totalCount: Scalars['Int']['output']; - transactionId: Scalars['String']['output']; }; export type TransactionResultEventsConnectionEdge = { @@ -828,7 +928,6 @@ export type TransactionResultTransfersConnection = { edges: Array>; pageInfo: PageInfo; totalCount: Scalars['Int']['output']; - transactionId?: Maybe; }; export type TransactionResultTransfersConnectionEdge = { @@ -837,21 +936,35 @@ export type TransactionResultTransfersConnectionEdge = { node: Transfer; }; +/** List of capabilities associated with/installed by this signer. */ export type TransactionSignature = { __typename?: 'TransactionSignature'; sig: Scalars['String']['output']; }; +/** A transfer of funds from a fungible between two accounts. */ export type Transfer = Node & { __typename?: 'Transfer'; amount: Scalars['Decimal']['output']; block: Block; + /** @deprecated Use `block.hash` field instead. */ + blockHash: Scalars['String']['output']; + /** @deprecated Use `block.chainId` field instead. */ + chainId: Scalars['BigInt']['output']; + creationTime: Scalars['DateTime']['output']; + /** The counterpart of the crosschain-transfer. `null` when it is not a cross-chain-transfer. */ crossChainTransfer?: Maybe; + /** @deprecated Use `block.height` field instead. */ + height: Scalars['BigInt']['output']; id: Scalars['ID']['output']; + moduleHash: Scalars['String']['output']; moduleName: Scalars['String']['output']; + /** The order of the transfer when it is a `defpact` (multi-step transaction) execution. */ orderIndex: Scalars['BigInt']['output']; receiverAccount: Scalars['String']['output']; + requestKey: Scalars['String']['output']; senderAccount: Scalars['String']['output']; + /** The transaction that initiated this transfer. */ transaction?: Maybe; }; @@ -930,7 +1043,8 @@ export type ResolversUnionTypes<_RefType extends Record> = { /** Mapping of interface types */ export type ResolversInterfaceTypes<_RefType extends Record> = { - Node: ( Omit & { events: _RefType['BlockEventsConnection'], minerAccount: _RefType['FungibleChainAccount'], parent?: Maybe<_RefType['Block']>, transactions: _RefType['BlockTransactionsConnection'] } ) | ( Omit & { block: _RefType['Block'], transaction?: Maybe<_RefType['Transaction']> } ) | ( Omit & { chainAccounts: Array<_RefType['FungibleChainAccount']>, transactions: _RefType['FungibleAccountTransactionsConnection'], transfers: _RefType['FungibleAccountTransfersConnection'] } ) | ( Omit & { guard: _RefType['Guard'], transactions: _RefType['FungibleChainAccountTransactionsConnection'], transfers: _RefType['FungibleChainAccountTransfersConnection'] } ) | ( Omit & { transactions: _RefType['NonFungibleAccountTransactionsConnection'] } ) | ( Omit & { transactions: _RefType['NonFungibleChainAccountTransactionsConnection'] } ) | ( NonFungibleTokenBalance ) | ( Signer ) | ( Omit & { cmd: _RefType['TransactionCommand'], orphanedTransactions?: Maybe>>, result: _RefType['TransactionInfo'] } ) | ( Omit & { block: _RefType['Block'], crossChainTransfer?: Maybe<_RefType['Transfer']>, transaction?: Maybe<_RefType['Transaction']> } ); + IGuard: ( KeysetGuard ); + Node: ( Omit & { events: _RefType['BlockEventsConnection'], minerAccount: _RefType['FungibleChainAccount'], parent?: Maybe<_RefType['Block']>, transactions: _RefType['BlockTransactionsConnection'] } ) | ( Omit & { block: _RefType['Block'], transaction?: Maybe<_RefType['Transaction']> } ) | ( Omit & { chainAccounts: Array<_RefType['FungibleChainAccount']>, transactions: _RefType['FungibleAccountTransactionsConnection'], transfers: _RefType['FungibleAccountTransfersConnection'] } ) | ( Omit & { guard: _RefType['IGuard'], transactions: _RefType['FungibleChainAccountTransactionsConnection'], transfers: _RefType['FungibleChainAccountTransfersConnection'] } ) | ( Omit & { chainAccounts: Array<_RefType['NonFungibleChainAccount']>, nonFungibleTokenBalances: Array<_RefType['NonFungibleTokenBalance']>, transactions: _RefType['NonFungibleAccountTransactionsConnection'] } ) | ( Omit & { nonFungibleTokenBalances: Array<_RefType['NonFungibleTokenBalance']>, transactions: _RefType['NonFungibleChainAccountTransactionsConnection'] } ) | ( Omit & { guard: _RefType['IGuard'] } ) | ( Signer ) | ( Omit & { cmd: _RefType['TransactionCommand'], orphanedTransactions?: Maybe>>, result: _RefType['TransactionInfo'] } ) | ( Omit & { block: _RefType['Block'], crossChainTransfer?: Maybe<_RefType['Transfer']>, transaction?: Maybe<_RefType['Transaction']> } ); }; /** Mapping between all available schema types and the resolvers types */ @@ -954,7 +1068,7 @@ export type ResolversTypes = { FungibleAccountTransactionsConnectionEdge: ResolverTypeWrapper & { node: ResolversTypes['Transaction'] }>; FungibleAccountTransfersConnection: ResolverTypeWrapper & { edges: Array }>; FungibleAccountTransfersConnectionEdge: ResolverTypeWrapper & { node: ResolversTypes['Transfer'] }>; - FungibleChainAccount: ResolverTypeWrapper & { guard: ResolversTypes['Guard'], transactions: ResolversTypes['FungibleChainAccountTransactionsConnection'], transfers: ResolversTypes['FungibleChainAccountTransfersConnection'] }>; + FungibleChainAccount: ResolverTypeWrapper & { guard: ResolversTypes['IGuard'], transactions: ResolversTypes['FungibleChainAccountTransactionsConnection'], transfers: ResolversTypes['FungibleChainAccountTransfersConnection'] }>; FungibleChainAccountTransactionsConnection: ResolverTypeWrapper & { edges: Array }>; FungibleChainAccountTransactionsConnectionEdge: ResolverTypeWrapper & { node: ResolversTypes['Transaction'] }>; FungibleChainAccountTransfersConnection: ResolverTypeWrapper & { edges: Array }>; @@ -962,19 +1076,20 @@ export type ResolversTypes = { GasLimitEstimation: ResolverTypeWrapper; GenesisHeight: ResolverTypeWrapper; GraphConfiguration: ResolverTypeWrapper; - Guard: ResolverTypeWrapper; ID: ResolverTypeWrapper; + IGuard: ResolverTypeWrapper['IGuard']>; Int: ResolverTypeWrapper; + KeysetGuard: ResolverTypeWrapper; NetworkInfo: ResolverTypeWrapper; Node: ResolverTypeWrapper['Node']>; - NonFungibleAccount: ResolverTypeWrapper & { transactions: ResolversTypes['NonFungibleAccountTransactionsConnection'] }>; + NonFungibleAccount: ResolverTypeWrapper & { chainAccounts: Array, nonFungibleTokenBalances: Array, transactions: ResolversTypes['NonFungibleAccountTransactionsConnection'] }>; NonFungibleAccountTransactionsConnection: ResolverTypeWrapper & { edges: Array }>; NonFungibleAccountTransactionsConnectionEdge: ResolverTypeWrapper & { node: ResolversTypes['Transaction'] }>; - NonFungibleChainAccount: ResolverTypeWrapper & { transactions: ResolversTypes['NonFungibleChainAccountTransactionsConnection'] }>; + NonFungibleChainAccount: ResolverTypeWrapper & { nonFungibleTokenBalances: Array, transactions: ResolversTypes['NonFungibleChainAccountTransactionsConnection'] }>; NonFungibleChainAccountTransactionsConnection: ResolverTypeWrapper & { edges: Array }>; NonFungibleChainAccountTransactionsConnectionEdge: ResolverTypeWrapper & { node: ResolversTypes['Transaction'] }>; NonFungibleToken: ResolverTypeWrapper; - NonFungibleTokenBalance: ResolverTypeWrapper; + NonFungibleTokenBalance: ResolverTypeWrapper & { guard: ResolversTypes['IGuard'] }>; PactQuery: PactQuery; PactQueryData: PactQueryData; PactQueryResponse: ResolverTypeWrapper; @@ -1034,7 +1149,7 @@ export type ResolversParentTypes = { FungibleAccountTransactionsConnectionEdge: Omit & { node: ResolversParentTypes['Transaction'] }; FungibleAccountTransfersConnection: Omit & { edges: Array }; FungibleAccountTransfersConnectionEdge: Omit & { node: ResolversParentTypes['Transfer'] }; - FungibleChainAccount: Omit & { guard: ResolversParentTypes['Guard'], transactions: ResolversParentTypes['FungibleChainAccountTransactionsConnection'], transfers: ResolversParentTypes['FungibleChainAccountTransfersConnection'] }; + FungibleChainAccount: Omit & { guard: ResolversParentTypes['IGuard'], transactions: ResolversParentTypes['FungibleChainAccountTransactionsConnection'], transfers: ResolversParentTypes['FungibleChainAccountTransfersConnection'] }; FungibleChainAccountTransactionsConnection: Omit & { edges: Array }; FungibleChainAccountTransactionsConnectionEdge: Omit & { node: ResolversParentTypes['Transaction'] }; FungibleChainAccountTransfersConnection: Omit & { edges: Array }; @@ -1042,19 +1157,20 @@ export type ResolversParentTypes = { GasLimitEstimation: GasLimitEstimation; GenesisHeight: GenesisHeight; GraphConfiguration: GraphConfiguration; - Guard: Guard; ID: Scalars['ID']['output']; + IGuard: ResolversInterfaceTypes['IGuard']; Int: Scalars['Int']['output']; + KeysetGuard: KeysetGuard; NetworkInfo: NetworkInfo; Node: ResolversInterfaceTypes['Node']; - NonFungibleAccount: Omit & { transactions: ResolversParentTypes['NonFungibleAccountTransactionsConnection'] }; + NonFungibleAccount: Omit & { chainAccounts: Array, nonFungibleTokenBalances: Array, transactions: ResolversParentTypes['NonFungibleAccountTransactionsConnection'] }; NonFungibleAccountTransactionsConnection: Omit & { edges: Array }; NonFungibleAccountTransactionsConnectionEdge: Omit & { node: ResolversParentTypes['Transaction'] }; - NonFungibleChainAccount: Omit & { transactions: ResolversParentTypes['NonFungibleChainAccountTransactionsConnection'] }; + NonFungibleChainAccount: Omit & { nonFungibleTokenBalances: Array, transactions: ResolversParentTypes['NonFungibleChainAccountTransactionsConnection'] }; NonFungibleChainAccountTransactionsConnection: Omit & { edges: Array }; NonFungibleChainAccountTransactionsConnectionEdge: Omit & { node: ResolversParentTypes['Transaction'] }; NonFungibleToken: NonFungibleToken; - NonFungibleTokenBalance: NonFungibleTokenBalance; + NonFungibleTokenBalance: Omit & { guard: ResolversParentTypes['IGuard'] }; PactQuery: PactQuery; PactQueryData: PactQueryData; PactQueryResponse: PactQueryResponse; @@ -1100,9 +1216,9 @@ export interface BigIntScalarConfig extends GraphQLScalarTypeConfig = { chainId?: Resolver; creationTime?: Resolver; - difficulty?: Resolver, ParentType, ContextType>; + difficulty?: Resolver; epoch?: Resolver; - events?: Resolver>; + events?: Resolver>; flags?: Resolver; hash?: Resolver; height?: Resolver; @@ -1114,7 +1230,7 @@ export type BlockResolvers; powHash?: Resolver; target?: Resolver; - transactions?: Resolver>; + transactions?: Resolver>; weight?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; @@ -1174,8 +1290,9 @@ export type EventResolvers; id?: Resolver; moduleName?: Resolver; - name?: Resolver, ParentType, ContextType>; + name?: Resolver; orderIndex?: Resolver; + parameterText?: Resolver; parameters?: Resolver, ParentType, ContextType>; qualifiedName?: Resolver; requestKey?: Resolver; @@ -1195,8 +1312,8 @@ export type FungibleAccountResolvers; id?: Resolver; totalBalance?: Resolver; - transactions?: Resolver>; - transfers?: Resolver>; + transactions?: Resolver>; + transfers?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -1231,10 +1348,10 @@ export type FungibleChainAccountResolvers; chainId?: Resolver; fungibleName?: Resolver; - guard?: Resolver; + guard?: Resolver; id?: Resolver; - transactions?: Resolver>; - transfers?: Resolver>; + transactions?: Resolver>; + transfers?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -1281,10 +1398,18 @@ export type GenesisHeightResolvers = { minimumBlockHeight?: Resolver, ParentType, ContextType>; + version?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type GuardResolvers = { +export type IGuardResolvers = { + __resolveType: TypeResolveFn<'KeysetGuard', ParentType, ContextType>; + keys?: Resolver, ParentType, ContextType>; + predicate?: Resolver; + raw?: Resolver; +}; + +export type KeysetGuardResolvers = { keys?: Resolver, ParentType, ContextType>; predicate?: Resolver; raw?: Resolver; @@ -1316,9 +1441,10 @@ export type NodeResolvers = { accountName?: Resolver; + chainAccounts?: Resolver, ParentType, ContextType>; id?: Resolver; nonFungibleTokenBalances?: Resolver, ParentType, ContextType>; - transactions?: Resolver>; + transactions?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -1340,7 +1466,7 @@ export type NonFungibleChainAccountResolvers; id?: Resolver; nonFungibleTokenBalances?: Resolver, ParentType, ContextType>; - transactions?: Resolver>; + transactions?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -1368,6 +1494,7 @@ export type NonFungibleTokenBalanceResolvers; balance?: Resolver; chainId?: Resolver; + guard?: Resolver; id?: Resolver; info?: Resolver, ParentType, ContextType>; tokenId?: Resolver; @@ -1394,13 +1521,13 @@ export type PageInfoResolvers = { block?: Resolver, ParentType, ContextType, RequireFields>; - blocksFromDepth?: Resolver, ParentType, ContextType, RequireFields>; - blocksFromHeight?: Resolver>; - completedBlockHeights?: Resolver>; - events?: Resolver>; + blocksFromDepth?: Resolver, ParentType, ContextType, RequireFields>; + blocksFromHeight?: Resolver>; + completedBlockHeights?: Resolver>; + events?: Resolver>; fungibleAccount?: Resolver, ParentType, ContextType, RequireFields>; fungibleAccountsByPublicKey?: Resolver, ParentType, ContextType, RequireFields>; - fungibleChainAccount?: Resolver, ParentType, ContextType, RequireFields>; + fungibleChainAccounts?: Resolver, ParentType, ContextType, RequireFields>; fungibleChainAccountsByPublicKey?: Resolver, ParentType, ContextType, RequireFields>; gasLimitEstimate?: Resolver, ParentType, ContextType, RequireFields>; graphConfiguration?: Resolver; @@ -1412,9 +1539,9 @@ export type QueryResolvers, ParentType, ContextType, RequireFields>; pactQuery?: Resolver, ParentType, ContextType, RequireFields>; transaction?: Resolver, ParentType, ContextType, RequireFields>; - transactions?: Resolver>; - transactionsByPublicKey?: Resolver>; - transfers?: Resolver>; + transactions?: Resolver>; + transactionsByPublicKey?: Resolver>; + transfers?: Resolver>; }; export type QueryBlocksFromDepthConnectionResolvers = { @@ -1517,8 +1644,8 @@ export type SignerResolvers = { events?: SubscriptionResolver>, "events", ParentType, ContextType, RequireFields>; - newBlocks?: SubscriptionResolver>, "newBlocks", ParentType, ContextType, RequireFields>; - newBlocksFromDepth?: SubscriptionResolver>, "newBlocksFromDepth", ParentType, ContextType, RequireFields>; + newBlocks?: SubscriptionResolver>, "newBlocks", ParentType, ContextType, Partial>; + newBlocksFromDepth?: SubscriptionResolver>, "newBlocksFromDepth", ParentType, ContextType, RequireFields>; transaction?: SubscriptionResolver, "transaction", ParentType, ContextType, RequireFields>; }; @@ -1575,13 +1702,13 @@ export type TransactionResultResolvers; continuation?: Resolver, ParentType, ContextType>; eventCount?: Resolver, ParentType, ContextType>; - events?: Resolver>; + events?: Resolver>; gas?: Resolver; goodResult?: Resolver, ParentType, ContextType>; - height?: Resolver; logs?: Resolver, ParentType, ContextType>; + metadata?: Resolver; transactionId?: Resolver, ParentType, ContextType>; - transfers?: Resolver>; + transfers?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -1589,7 +1716,6 @@ export type TransactionResultEventsConnectionResolvers>, ParentType, ContextType>; pageInfo?: Resolver; totalCount?: Resolver; - transactionId?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; @@ -1603,7 +1729,6 @@ export type TransactionResultTransfersConnectionResolvers>, ParentType, ContextType>; pageInfo?: Resolver; totalCount?: Resolver; - transactionId?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -1621,11 +1746,17 @@ export type TransactionSignatureResolvers = { amount?: Resolver; block?: Resolver; + blockHash?: Resolver; + chainId?: Resolver; + creationTime?: Resolver; crossChainTransfer?: Resolver, ParentType, ContextType>; + height?: Resolver; id?: Resolver; + moduleHash?: Resolver; moduleName?: Resolver; orderIndex?: Resolver; receiverAccount?: Resolver; + requestKey?: Resolver; senderAccount?: Resolver; transaction?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -1657,7 +1788,8 @@ export type Resolvers = { GasLimitEstimation?: GasLimitEstimationResolvers; GenesisHeight?: GenesisHeightResolvers; GraphConfiguration?: GraphConfigurationResolvers; - Guard?: GuardResolvers; + IGuard?: IGuardResolvers; + KeysetGuard?: KeysetGuardResolvers; NetworkInfo?: NetworkInfoResolvers; Node?: NodeResolvers; NonFungibleAccount?: NonFungibleAccountResolvers; diff --git a/indexer/src/kadena-server/config/schema.graphql b/indexer/src/kadena-server/config/schema.graphql index 4fd27ef5..9e32a132 100644 --- a/indexer/src/kadena-server/config/schema.graphql +++ b/indexer/src/kadena-server/config/schema.graphql @@ -14,54 +14,102 @@ Floats that will have a value of 0 or more. scalar Decimal type Subscription { - newBlocks(chainIds: [String]!): [Block!] + """ + Subscribe to new blocks. + """ + newBlocks(chainIds: [String!]): [Block!] + """ + Listen for a transaction by request key. + """ transaction(chainId: String, requestKey: String!): Transaction + """ + Listen for events by qualifiedName (e.g. `coin.TRANSFER`). +   + The parametersFilter is a stringified JSON object that matches the [JSON object property filters](https://www.prisma.io/docs/orm/prisma-client/special-fields-and-types/working-with-json-fields#filter-on-object-property) from Prisma. +   + An example of such a filter parameter value: `events(parametersFilter: "{\"array_starts_with\": \"k:abcdefg\"}")` + """ events( chainId: String minimumDepth: Int + parametersFilter: String qualifiedEventName: String! ): [Event!] - newBlocksFromDepth(chainIds: [String!]!, minimumDepth: Int!): [Block!] + """ + Subscribe to new blocks from a specific depth. + """ + newBlocksFromDepth(chainIds: [String!], minimumDepth: Int!): [Block!] } type Query { + """ + Retrieve a block by hash. + """ block(hash: String!): Block + """ + Retrieve blocks by chain and minimal depth. Default page size is 20. + """ blocksFromDepth( after: String before: String + """ + Default: all chains + """ chainIds: [String!] - first: Int = 20 - last: Int = 20 + first: Int + last: Int minimumDepth: Int! ): QueryBlocksFromDepthConnection + """ + Retrieve blocks by chain and minimal height. Default page size is 20. + """ blocksFromHeight( after: String before: String + """ + Default: all chains + """ chainIds: [String!] endHeight: Int - first: Int = 20 - last: Int = 20 + first: Int + last: Int startHeight: Int! ): QueryBlocksFromHeightConnection! + """ + Retrieve all completed blocks from a given height. Default page size is 20. + """ completedBlockHeights( after: String before: String + """ + Default: all chains + """ chainIds: [String!] + """ + Retrieve blocks from completed heights only. Default: true + """ completedHeights: Boolean = true - first: Int = 20 + first: Int heightCount: Int = 3 - last: Int = 20 + last: Int ): QueryCompletedBlockHeightsConnection! + """ + Retrieve events by qualifiedName (e.g. `coin.TRANSFER`). Default page size is 20. + + The parametersFilter is a stringified JSON object that matches the [JSON object property filters](https://www.prisma.io/docs/orm/prisma-client/special-fields-and-types/working-with-json-fields#filter-on-object-property) from Prisma. + + An example of such a filter parameter value: `events(parametersFilter: "{\"array_starts_with\": \"k:abcdefg\"}")` + """ events( after: String before: String blockHash: String chainId: String - first: Int = 20 - last: Int = 20 + first: Int + last: Int maxHeight: Int minHeight: Int minimumDepth: Int @@ -90,7 +138,7 @@ type Query { """ Retrieve an account by its name and fungible, such as coin, on a specific chain. """ - fungibleChainAccount( + fungibleChainAccounts( accountName: String! chainIds: [String!] fungibleName: String = "coin" @@ -107,17 +155,17 @@ type Query { """ Estimate the gas limit for one or more transactions. Throws an error when the transaction fails or is invalid. The input accepts a JSON object and based on the parameters passed it will determine what type of format it is and return the gas limit estimation. The following types are supported: - - - `full-transaction`: A complete transaction object. Required parameters: `cmd`, `hash` and `sigs`. - - `stringified-command`: A JSON stringified command. Required parameters: `cmd`. It also optionally accepts `sigs`. - - `full-command`: A full command. Required parameters: `payload`, `meta` and `signers`. - - `partial-command`: A partial command. Required parameters: `payload` and either `meta` or `signers`. In case `meta` is not given, but `signers` is given, you can also add `chainId` as a parameter. - - `payload`: A just the payload of a command. Required parameters: `payload` and `chainId`. - - `code`: The code of an execution. Required parameters: `code` and `chainId`. - - Every type accepts an optional parameter called `networkId` to override the default value from the environment variables. - - Example of the input needed for a type `code` query: `gasLimitEstimate(input: "{\"code\":\"(coin.details \\\"k:1234\\\")\",\"chainId\":\"3\"}")` +   + - `full-transaction`: A complete transaction object. Required parameters: `cmd`, `hash` and `sigs`. + - `stringified-command`: A JSON stringified command. Required parameters: `cmd`. It also optionally accepts `sigs`. + - `full-command`: A full command. Required parameters: `payload`, `meta` and `signers`. + - `partial-command`: A partial command. Required parameters: `payload` and either `meta` or `signers`. In case `meta` is not given, but `signers` is given, you can also add `chainId` as a parameter. + - `payload`: A just the payload of a command. Required parameters: `payload` and `chainId`. + - `code`: The code of an execution. Required parameters: `code` and `chainId`. +   + Every type accepts an optional parameter called `networkId` to override the default value from the environment variables. +   + Example of the input needed for a type `code` query: `gasLimitEstimate(input: "{\"code\":\"(coin.details \\\"k:1234\\\")\",\"chainId\":\"3\"}")` """ gasLimitEstimate(input: [String!]!): [GasLimitEstimation!]! @@ -157,66 +205,88 @@ type Query { """ pactQuery(pactQuery: [PactQuery!]!): [PactQueryResponse!]! + """ + Retrieve one transaction by its unique key. Throws an error if multiple transactions are found. + """ transaction( blockHash: String minimumDepth: Int requestKey: String! ): Transaction + """ + Retrieve transactions. Default page size is 20. + At least one of accountName, fungibleName, blockHash, or requestKey must be provided. + """ transactions( accountName: String after: String before: String blockHash: String chainId: String - first: Int = 20 + first: Int fungibleName: String - last: Int = 20 + last: Int maxHeight: Int minHeight: Int minimumDepth: Int requestKey: String ): QueryTransactionsConnection! + """ + Retrieve all transactions by a given public key. + """ transactionsByPublicKey( after: String before: String - first: Int = 20 - last: Int = 20 + first: Int + last: Int publicKey: String! ): QueryTransactionsByPublicKeyConnection! + """ + Retrieve transfers. Default page size is 20. + """ transfers( accountName: String after: String before: String blockHash: String chainId: String - first: Int = 20 + first: Int fungibleName: String - last: Int = 20 + last: Int requestKey: String ): QueryTransfersConnection! } +""" +A fungible-specific account. +""" type FungibleAccount implements Node { id: ID! accountName: String! chainAccounts: [FungibleChainAccount!]! fungibleName: String! totalBalance: Decimal! + """ + Default page size is 20. + """ transactions( after: String before: String - first: Int = 20 - last: Int = 20 + first: Int + last: Int ): FungibleAccountTransactionsConnection! + """ + Default page size is 20. + """ transfers( after: String before: String - first: Int = 20 - last: Int = 20 + first: Int + last: Int ): FungibleAccountTransfersConnection! } @@ -242,17 +312,23 @@ type FungibleAccountTransfersConnectionEdge { node: Transfer! } +""" +A chain and non-fungible-specific account. +""" type NonFungibleChainAccount implements Node { id: ID! accountName: String! chainId: String! nonFungibleTokenBalances: [NonFungibleTokenBalance!]! + """ + Default page size is 20. Note that custom token related transactions are not included. + """ transactions( after: String before: String - first: Int = 20 - last: Int = 20 + first: Int + last: Int ): NonFungibleChainAccountTransactionsConnection! } @@ -266,16 +342,23 @@ type QueryCompletedBlockHeightsConnectionEdge { node: Block! } +""" +The token identifier and its balance. +""" type NonFungibleTokenBalance implements Node { accountName: String! balance: Int! chainId: String! + guard: IGuard! id: ID! info: NonFungibleToken tokenId: String! version: String! } +""" +Information related to a token. +""" type NonFungibleToken { precision: Int! supply: Int! @@ -346,13 +429,21 @@ type QueryTransfersConnectionEdge { node: Transfer! } -# The Block type which implements the Node interface +""" +A unit of information that stores a set of verified transactions. +""" type Block implements Node { id: ID! hash: String! chainId: BigInt! creationTime: DateTime! - difficulty: BigInt + """ + The difficulty of the block. + """ + difficulty: BigInt! + """ + The moment the difficulty is adjusted to maintain a block validation time of 30 seconds. + """ epoch: DateTime! flags: Decimal! height: BigInt! @@ -361,30 +452,40 @@ type Block implements Node { weight: String! target: String! neighbors: [BlockNeighbor!]! + """ + The proof of work hash. + """ powHash: String! parent: Block + """ + Default page size is 20. + """ events( after: String before: String - first: Int = 20 - last: Int = 20 + first: Int + last: Int ): BlockEventsConnection! minerAccount: FungibleChainAccount! + """ + Default page size is 20. + """ transactions( after: String before: String - first: Int = 20 - last: Int = 20 + first: Int + last: Int ): BlockTransactionsConnection! } -# Interface representing a node with an ID interface Node { id: ID! } -# The neighbor of a block. +""" +The neighbor of a block. +""" type BlockNeighbor { chainId: String! hash: String! @@ -408,44 +509,65 @@ type PageInfo { startCursor: String } +""" +An event emitted by the execution of a smart-contract function. +""" type Event implements Node { id: ID! - name: String + name: String! block: Block! chainId: BigInt! + """ + The height of the block where the event was emitted. + """ height: BigInt! moduleName: String! + """ + The order index of this event, in the case that there are multiple events in one transaction. + """ orderIndex: BigInt! requestKey: String! parameters: String + parameterText: String! + """ + The full eventname, containing module and eventname, e.g. coin.TRANSFER + """ qualifiedName: String! transaction: Transaction } -# A fungible specific chain-account. +""" +A fungible specific chain-account. +""" type FungibleChainAccount implements Node { id: ID! accountName: String! balance: Float! chainId: String! fungibleName: String! - guard: Guard! + guard: IGuard! + """ + Transactions that the current account is sender of. Default page size is 20. + """ transactions( after: String before: String - first: Int = 20 - last: Int = 20 + first: Int + last: Int ): FungibleChainAccountTransactionsConnection! + """ + Default page size is 20. + """ transfers( after: String before: String - first: Int = 20 - last: Int = 20 + first: Int + last: Int ): FungibleChainAccountTransfersConnection! } @@ -471,6 +593,9 @@ type BlockTransactionsConnectionEdge { node: Transaction! } +""" +A transaction. +""" type Transaction { id: ID! cmd: TransactionCommand! @@ -488,6 +613,9 @@ type Transaction implements Node { sigs: [TransactionSignature!]! } +""" +A transaction command. +""" type TransactionCommand { meta: TransactionMeta! """ @@ -500,6 +628,9 @@ type TransactionCommand { signers: [Signer!]! } +""" +The metadata of a transaction. +""" type TransactionMeta { chainId: BigInt! creationTime: DateTime! @@ -509,77 +640,114 @@ type TransactionMeta { ttl: BigInt! } +""" +The payload of a transaction. +""" union TransactionPayload = ContinuationPayload | ExecutionPayload +""" +The result of a transaction. +""" union TransactionInfo = TransactionMempoolInfo | TransactionResult -# List of capabilities associated with/installed by this signer. +""" +List of capabilities associated with/installed by this signer. +""" type TransactionSignature { sig: String! } +""" +The payload of an cont transaction. +""" type ContinuationPayload { - # The environment data made available to the transaction. Formatted as raw JSON. + """ + The environment data made available to the transaction. Formatted as raw JSON. + """ data: String! - # A unique id when a pact (defpact) is initiated. See the "Pact execution scope and pact-id" explanation in the docs for more information. + """ + A unique id when a pact (defpact) is initiated. See the "Pact execution scope and pact-id" explanation in the docs for more information. + """ pactId: String - # The proof provided to continue the cross-chain transaction. + """ + The proof provided to continue the cross-chain transaction. + """ proof: String - # Whether or not this transaction can be rolled back. + """ + Whether or not this transaction can be rolled back. + """ rollback: Boolean - # The step-number when this is an execution of a `defpact`, aka multi-step transaction. + """ + The step-number when this is an execution of a `defpact`, aka multi-step transaction. + """ step: Int } -# The payload of an exec transaction. +""" +The payload of an exec transaction. +""" type ExecutionPayload { - # The Pact expressions executed in this transaction when it is an `exec` transaction. + """ + The Pact expressions executed in this transaction when it is an `exec` transaction. + """ code: String - # The environment data made available to the transaction. Formatted as raw JSON. + """ + The environment data made available to the transaction. Formatted as raw JSON. + """ data: String! } -# The mempool information +""" +The mempool information. +""" type TransactionMempoolInfo { - # The status of the mempool. + """ + The status of the mempool. + """ status: String } -# The result of a transaction. +""" +The result of a transaction. +""" type TransactionResult { - # The transaction result when it was successful. Formatted as raw JSON. + """ + The transaction result when it was successful. Formatted as raw JSON. + """ badResult: String - - # The JSON stringified continuation in the case that it is a continuation. + """ + The JSON stringified continuation in the case that it is a continuation. + """ continuation: String eventCount: BigInt gas: BigInt! - - # The transaction result when it was successful. Formatted as raw JSON. + """ + The transaction result when it was successful. Formatted as raw JSON. + """ goodResult: String - - # The height of the block this transaction belongs to. - height: BigInt! - - # Identifier to retrieve the logs for the execution of the transaction. + """ + Identifier to retrieve the logs for the execution of the transaction. + """ logs: String transactionId: BigInt + metadata: String! @deprecated(reason: "Not used.") + block: Block! transfers( after: String before: String - first: Int = 20 - last: Int = 20 + first: Int + last: Int ): TransactionResultTransfersConnection! events( after: String before: String - first: Int = 20 - last: Int = 20 + first: Int + last: Int ): TransactionResultEventsConnection! } @@ -587,8 +755,6 @@ type TransactionResultEventsConnection { edges: [TransactionResultEventsConnectionEdge]! pageInfo: PageInfo! - # used as parameter for the `totalCount` field - transactionId: String! totalCount: Int! } @@ -601,9 +767,6 @@ type TransactionResultTransfersConnection { edges: [TransactionResultTransfersConnectionEdge]! pageInfo: PageInfo! totalCount: Int! - - # used as parameter for the `totalCount` field - transactionId: String } type TransactionResultTransfersConnectionEdge { @@ -611,21 +774,27 @@ type TransactionResultTransfersConnectionEdge { node: Transfer! } -# A signer for a specific transaction. +""" +A signer for a specific transaction. +""" type Signer implements Node { - # The signer for the gas. + """ + The signer for the gas. + """ address: String id: ID! orderIndex: Int pubkey: String! - - # The signature scheme that was used to sign. + """ + The signature scheme that was used to sign. + """ scheme: String clist: [TransactionCapability!]! } - -# List of capabilities associated with/installed by this signer. +""" +List of capabilities associated with/installed by this signer. +""" type TransactionCapability { args: String! name: String! @@ -642,30 +811,51 @@ type FungibleChainAccountTransfersConnectionEdge { node: Transfer! } +""" +A transfer of funds from a fungible between two accounts. +""" type Transfer implements Node { amount: Decimal! block: Block! + blockHash: String! @deprecated(reason: "Use `block.hash` field instead.") + chainId: BigInt! @deprecated(reason: "Use `block.chainId` field instead.") + creationTime: DateTime! - # The counterpart of the crosschain-transfer. `null` when it is not a cross-chain-transfer. + """ + The counterpart of the crosschain-transfer. `null` when it is not a cross-chain-transfer. + """ crossChainTransfer: Transfer + height: BigInt! @deprecated(reason: "Use `block.height` field instead.") id: ID! + moduleHash: String! moduleName: String! - # The order of the transfer when it is a `defpact` (multi-step transaction) execution. + """ + The order of the transfer when it is a `defpact` (multi-step transaction) execution. + """ orderIndex: BigInt! receiverAccount: String! + requestKey: String! senderAccount: String! - # The transaction that initiated this transfer. + """ + The transaction that initiated this transfer. + """ transaction: Transaction } -# General information about the graph and chainweb-data. +""" +General information about the graph and chainweb-data. +""" type GraphConfiguration { """ The lowest block-height that is indexed in this endpoint. """ minimumBlockHeight: BigInt + """ + The version of the graphl api. + """ + version: String! } """ @@ -756,6 +946,7 @@ A non-fungible-specific account. """ type NonFungibleAccount implements Node { accountName: String! + chainAccounts: [NonFungibleChainAccount!]! id: ID! nonFungibleTokenBalances: [NonFungibleTokenBalance!]! @@ -765,8 +956,8 @@ type NonFungibleAccount implements Node { transactions( after: String before: String - first: Int = 20 - last: Int = 20 + first: Int + last: Int ): NonFungibleAccountTransactionsConnection! } @@ -792,7 +983,40 @@ type NonFungibleChainAccountTransactionsConnectionEdge { node: Transaction! } -type Guard { +""" +A chain and non-fungible-specific account. +""" +type NonFungibleChainAccount implements Node { + accountName: String! + chainId: String! + id: ID! + nonFungibleTokenBalances: [NonFungibleTokenBalance!]! + + """ + Default page size is 20. Note that custom token related transactions are not included. + """ + transactions( + after: String + before: String + first: Int + last: Int + ): NonFungibleChainAccountTransactionsConnection! +} + +""" +A guard. This is a union of all the different types of guards that can be used in a pact. +""" +interface IGuard { + keys: [String!]! @deprecated(reason: "deprecated, use KeysetGuard.keys") + predicate: String! + @deprecated(reason: "deprecated, use KeysetGuard.predicate") + raw: String! +} + +""" +A keyset guard. +""" +type KeysetGuard implements IGuard { keys: [String!]! predicate: String! raw: String! diff --git a/indexer/src/kadena-server/repository/application/balance-repository.ts b/indexer/src/kadena-server/repository/application/balance-repository.ts index 78cc69e0..6292d417 100644 --- a/indexer/src/kadena-server/repository/application/balance-repository.ts +++ b/indexer/src/kadena-server/repository/application/balance-repository.ts @@ -14,6 +14,7 @@ export interface INonFungibleTokenBalance { export interface INonFungibleAccount { id: string; accountName: string; + chainAccounts: INonFungibleChainAccount[]; nonFungibleTokenBalances: Array; } diff --git a/indexer/src/kadena-server/repository/application/transaction-repository.ts b/indexer/src/kadena-server/repository/application/transaction-repository.ts index 9b263092..ea9d6402 100644 --- a/indexer/src/kadena-server/repository/application/transaction-repository.ts +++ b/indexer/src/kadena-server/repository/application/transaction-repository.ts @@ -34,7 +34,7 @@ export interface GetTransactionsByRequestKey { export type TransactionOutput = Omit & { cmd: Omit; -} & { transactionId: string; blockHash: string; blockHeight: number }; +} & { databaseTransactionId: string; blockHash: string; blockHeight: number }; export type TransactionMetaOutput = TransactionMeta; diff --git a/indexer/src/kadena-server/repository/infra/repository/balance-db-repository.ts b/indexer/src/kadena-server/repository/infra/repository/balance-db-repository.ts index 13ecefd9..9495be9d 100644 --- a/indexer/src/kadena-server/repository/infra/repository/balance-db-repository.ts +++ b/indexer/src/kadena-server/repository/infra/repository/balance-db-repository.ts @@ -21,7 +21,7 @@ import { fungibleChainAccountValidator } from "../schema-validator/fungible-chai import { nonFungibleTokenBalanceValidator } from "../schema-validator/non-fungible-token-balance-validator"; export default class BalanceDbRepository implements BalanceRepository { - async getAccountInfo(accountName: string, fungibleName?: string) { + async getAccountInfo(accountName: string, fungibleName = "coin") { const account = await BalanceModel.findOne({ where: { account: accountName, @@ -196,6 +196,8 @@ export default class BalanceDbRepository implements BalanceRepository { return { id: getNonFungibleAccountBase64ID(accountName), accountName, + // TODO + chainAccounts: [], nonFungibleTokenBalances, }; } @@ -361,7 +363,7 @@ export default class BalanceDbRepository implements BalanceRepository { ]); if (!guardRows?.length) { - const params = [publicKey, fungibleName]; + const params = [`k:${publicKey}`, fungibleName]; const query = ` SELECT b.account, b."chainId" FROM "Balances" b @@ -387,7 +389,7 @@ export default class BalanceDbRepository implements BalanceRepository { const totalBalance = balancesNumber.reduce((acc, cur) => acc + cur, 0); const accountInfo = fungibleAccountValidator.mapFromSequelize({ - account: publicKey, + account: `k:${publicKey}`, module: fungibleName, chainId: -1, balance: BigInt(-1), @@ -462,7 +464,11 @@ export default class BalanceDbRepository implements BalanceRepository { publicKey, ]); - const params = [publicKey, fungibleName, chainId]; + const params = [ + guardRows?.length ? publicKey : `k:${publicKey}`, + fungibleName, + chainId, + ]; let query = ""; if (!guardRows?.length) { query = ` diff --git a/indexer/src/kadena-server/repository/infra/repository/block-db-repository.ts b/indexer/src/kadena-server/repository/infra/repository/block-db-repository.ts index 586fffaf..7c97480c 100644 --- a/indexer/src/kadena-server/repository/infra/repository/block-db-repository.ts +++ b/indexer/src/kadena-server/repository/infra/repository/block-db-repository.ts @@ -7,7 +7,7 @@ import BlockRepository, { GetBlocksFromDepthParams, GetCompletedBlocksParams, } from "../../application/block-repository"; -import { getPageInfo } from "../../pagination"; +import { getPageInfo, getPaginationParams } from "../../pagination"; import { blockValidator } from "../schema-validator/block-schema-validator"; import Balance from "../../../../models/balance"; import { handleSingleQuery } from "../../../utils/raw-query"; @@ -30,17 +30,31 @@ export default class BlockDbRepository implements BlockRepository { } async getBlocksFromDepth(params: GetBlocksFromDepthParams) { - const { minimumDepth, after, before, first, last, chainIds } = params; + const { + minimumDepth, + after: afterEncoded, + before: beforeEncoded, + first, + last, + chainIds, + } = params; + + const { limit, order, after, before } = getPaginationParams({ + after: afterEncoded, + before: beforeEncoded, + first, + last, + }); const query: FindOptions = { where: { height: { [Op.gt]: minimumDepth ?? 0 }, - ...(after && { id: { [Op.gt]: after } }), - ...(before && { id: { [Op.lt]: before } }), + ...(after && { id: { [Op.lt]: after } }), + ...(before && { id: { [Op.gt]: before } }), ...(!!chainIds?.length && { chainId: { [Op.in]: chainIds } }), }, - limit: before ? last : first, - order: [["height", before ? "DESC" : "ASC"]], + limit, + order: [["id", order]], }; const rows = await BlockModel.findAll(query); @@ -50,28 +64,38 @@ export default class BlockDbRepository implements BlockRepository { node: blockValidator.mapFromSequelize(row), })); - const pageInfo = getPageInfo({ rows: edges, first, last }); - - return { - edges, - pageInfo, - }; + const pageInfo = getPageInfo({ edges, order, limit, after, before }); + return pageInfo; } async getBlocksBetweenHeights(params: GetBlocksBetweenHeightsParams) { - const { startHeight, endHeight, after, before, first, chainIds, last } = - params; + const { + startHeight, + endHeight, + after: afterEncoded, + before: beforeEncoded, + first, + chainIds, + last, + } = params; + + const { limit, order, after, before } = getPaginationParams({ + after: afterEncoded, + before: beforeEncoded, + first, + last, + }); const query: FindOptions = { where: { height: { [Op.gte]: startHeight }, ...(endHeight && { height: { [Op.lte]: endHeight } }), - ...(after && { id: { [Op.gt]: after } }), - ...(before && { id: { [Op.lt]: before } }), + ...(after && { id: { [Op.lt]: after } }), + ...(before && { id: { [Op.gt]: before } }), ...(!!chainIds?.length && { chainId: { [Op.in]: chainIds } }), }, - limit: before ? last : first, - order: [["height", before ? "DESC" : "ASC"]], + limit, + order: [["id", order]], }; const rows = await BlockModel.findAll(query); @@ -81,12 +105,8 @@ export default class BlockDbRepository implements BlockRepository { node: blockValidator.mapFromSequelize(row), })); - const pageInfo = getPageInfo({ rows: edges, first, last }); - - return { - edges, - pageInfo, - }; + const pageInfo = getPageInfo({ edges, order, limit, after, before }); + return pageInfo; } async getMinerData(hash: string, chainId: string) { @@ -138,13 +158,20 @@ export default class BlockDbRepository implements BlockRepository { const { first, last, - before, - after, + before: beforeEncoded, + after: afterEncoded, chainIds: chainIdsParam, completedHeights, heightCount, } = params; + const { limit, order, after, before } = getPaginationParams({ + after: afterEncoded, + before: beforeEncoded, + first, + last, + }); + const chainIds = chainIdsParam?.length ? chainIdsParam : await this.getChainIds(); @@ -168,7 +195,7 @@ export default class BlockDbRepository implements BlockRepository { if (totalCompletedHeights.length > 0) { const queryParams: any[] = [ - before ? last : first, + limit, chainIds, totalCompletedHeights, totalCompletedHeights[0], @@ -178,12 +205,12 @@ export default class BlockDbRepository implements BlockRepository { if (after) { queryParams.push(after); - conditions += "\nAND id > $5"; + conditions += "\nAND id < $5"; } if (before) { queryParams.push(before); - conditions += "\nAND id < $5"; + conditions += "\nAND id > $5"; } let queryOne = ` @@ -192,7 +219,7 @@ export default class BlockDbRepository implements BlockRepository { WHERE "chainId" = ANY($2) AND (height = ANY($3) OR height > $4) ${conditions} - ORDER BY height ${before ? "DESC" : "ASC"} + ORDER BY id ${order} LIMIT $1 `; @@ -205,12 +232,9 @@ export default class BlockDbRepository implements BlockRepository { cursor: row.id.toString(), node: blockValidator.validate(row), })); - const pageInfo = getPageInfo({ rows: edges, first, last }); - return { - edges, - pageInfo, - }; + const pageInfo = getPageInfo({ edges, order, limit, after, before }); + return pageInfo; } } @@ -229,22 +253,18 @@ export default class BlockDbRepository implements BlockRepository { const totalCompletedHeights = heightRows.map((r) => r.height) as number[]; - const queryParams: any[] = [ - before ? last : first, - chainIds, - totalCompletedHeights, - ]; + const queryParams: any[] = [limit, chainIds, totalCompletedHeights]; let conditions = ""; if (after) { queryParams.push(after); - conditions += "\nAND id > $4"; + conditions += "\nAND id < $4"; } if (before) { queryParams.push(before); - conditions += "\nAND id < $4"; + conditions += "\nAND id > $4"; } let queryThree = ` @@ -253,7 +273,7 @@ export default class BlockDbRepository implements BlockRepository { WHERE "chainId" = ANY($2) AND height = ANY($3) ${conditions} - ORDER BY height ${before ? "DESC" : "ASC"} + ORDER BY id ${order} LIMIT $1 `; @@ -263,12 +283,9 @@ export default class BlockDbRepository implements BlockRepository { cursor: row.id.toString(), node: blockValidator.validate(row), })); - const pageInfo = getPageInfo({ rows: edges, first, last }); - return { - edges, - pageInfo, - }; + const pageInfo = getPageInfo({ edges, order, limit, after, before }); + return pageInfo; } async getBlocksByEventIds(eventIds: readonly string[]) { diff --git a/indexer/src/kadena-server/repository/infra/repository/event-db-repository.ts b/indexer/src/kadena-server/repository/infra/repository/event-db-repository.ts index bbb8869b..364e6310 100644 --- a/indexer/src/kadena-server/repository/infra/repository/event-db-repository.ts +++ b/indexer/src/kadena-server/repository/infra/repository/event-db-repository.ts @@ -9,14 +9,14 @@ import EventRepository, { GetTotalTransactionEventsCount, GetTransactionEventsParams, } from "../../application/event-repository"; -import { getPageInfo } from "../../pagination"; +import { getPageInfo, getPaginationParams } from "../../pagination"; import { ConnectionEdge } from "../../types"; import { eventValidator } from "../schema-validator/event-schema-validator"; export default class EventDbRepository implements EventRepository { async getEvent(params: GetEventParams): Promise { const { hash, requestKey, orderIndex } = params; - const queryParams = [hash, requestKey]; + const queryParams = [hash, requestKey, orderIndex]; const query = ` SELECT e.id as id, @@ -24,6 +24,7 @@ export default class EventDbRepository implements EventRepository { e.requestkey as "requestKey", b."chainId" as "chainId", b.height as height, + e."orderIndex" as "orderIndex", e.module as "moduleName", e.params as parameters, b.hash as "blockHash" @@ -32,9 +33,9 @@ export default class EventDbRepository implements EventRepository { JOIN "Events" e ON t.id = e."transactionId" WHERE b.hash = $1 AND e.requestkey = $2 + AND e."orderIndex" = $3 LIMIT 1; `; - // AND e."orderIndex" = $3 TODO (STREAMING) const { rows } = await rootPgPool.query(query, queryParams); @@ -42,19 +43,33 @@ export default class EventDbRepository implements EventRepository { return output; } async getBlockEvents(params: GetBlockEventsParams) { - const { hash, after, before, first, last } = params; - const queryParams = [before ? last : first, hash]; + const { + hash, + after: afterEncoded, + before: beforeEncoded, + first, + last, + } = params; + + const { limit, order, after, before } = getPaginationParams({ + after: afterEncoded, + before: beforeEncoded, + first, + last, + }); + + const queryParams = [limit, hash]; let conditions = ""; if (before) { queryParams.push(before); - conditions += `\nAND e.id < $3`; + conditions += `\nAND e.id > $3`; } if (after) { queryParams.push(after); - conditions += `\nAND e.id > $3`; + conditions += `\nAND e.id < $3`; } const query = ` @@ -63,6 +78,7 @@ export default class EventDbRepository implements EventRepository { e.requestkey as "requestKey", b."chainId" as "chainId", b.height as height, + e."orderIndex" as "orderIndex", e.module as "moduleName", e.params as parameters, b.hash as "blockHash" @@ -71,7 +87,7 @@ export default class EventDbRepository implements EventRepository { JOIN "Events" e ON t.id = e."transactionId" WHERE b.hash = $2 ${conditions} - ORDER BY e.id ${before ? "DESC" : "ASC"} + ORDER BY e.id ${order} LIMIT $1; `; @@ -82,12 +98,8 @@ export default class EventDbRepository implements EventRepository { node: eventValidator.validate(row), })); - const pageInfo = getPageInfo({ rows: edges, first, last }); - - return { - edges, - pageInfo, - }; + const pageInfo = getPageInfo({ edges, order, limit, after, before }); + return pageInfo; } async getTotalCountOfBlockEvents(hash: string): Promise { @@ -112,8 +124,8 @@ export default class EventDbRepository implements EventRepository { qualifiedEventName, blockHash, chainId, - after, - before, + after: afterEncoded, + before: beforeEncoded, first, last, minHeight, @@ -122,9 +134,18 @@ export default class EventDbRepository implements EventRepository { requestKey, } = params; - const [module, name] = qualifiedEventName.split("."); + const { limit, order, after, before } = getPaginationParams({ + after: afterEncoded, + before: beforeEncoded, + first, + last, + }); - const queryParams: (string | number)[] = [before ? last : first]; + const splitted = qualifiedEventName.split("."); + const name = splitted.pop() ?? ""; + const module = splitted.join("."); + + const queryParams: (string | number)[] = [limit]; let conditions = ""; queryParams.push(module); @@ -164,12 +185,12 @@ export default class EventDbRepository implements EventRepository { if (before) { queryParams.push(before); - conditions += `\nAND e.id < $${queryParams.length}`; + conditions += `\nAND e.id > $${queryParams.length}`; } if (after) { queryParams.push(after); - conditions += `\nAND e.id > $${queryParams.length}`; + conditions += `\nAND e.id < $${queryParams.length}`; } const query = ` @@ -177,6 +198,7 @@ export default class EventDbRepository implements EventRepository { t.requestkey as "requestKey", b."chainId" as "chainId", b.height as height, + e."orderIndex" as "orderIndex", e.module as "moduleName", e.name as name, e.params as parameters, @@ -185,7 +207,7 @@ export default class EventDbRepository implements EventRepository { join "Transactions" t on e."transactionId" = t.id join "Blocks" b on b.id = t."blockId" ${conditions} - ORDER BY id ${before ? "DESC" : "ASC"} + ORDER BY id ${order} LIMIT $1 `; @@ -196,12 +218,8 @@ export default class EventDbRepository implements EventRepository { node: eventValidator.validate(row), })); - const pageInfo = getPageInfo({ rows: edges, first, last }); - - return { - edges, - pageInfo, - }; + const pageInfo = getPageInfo({ edges, order, limit, after, before }); + return pageInfo; } async getTotalEventsCount(params: GetTotalEventsCount): Promise { @@ -215,7 +233,9 @@ export default class EventDbRepository implements EventRepository { requestKey, } = params; - const [module, name] = qualifiedEventName.split("."); + const splitted = qualifiedEventName.split("."); + const name = splitted.pop() ?? ""; + const module = splitted.join("."); const queryParams: (string | number)[] = []; let conditions = ""; @@ -274,22 +294,32 @@ export default class EventDbRepository implements EventRepository { async getTransactionEvents( params: GetTransactionEventsParams, ): Promise<{ pageInfo: PageInfo; edges: ConnectionEdge[] }> { - const { transactionId, after, before, first, last } = params; - - const queryParams: (string | number)[] = [ - before ? last : first, + const { transactionId, - ]; + after: afterEncoded, + before: beforeEncoded, + first, + last, + } = params; + + const { limit, order, after, before } = getPaginationParams({ + after: afterEncoded, + before: beforeEncoded, + first, + last, + }); + + const queryParams: (string | number)[] = [limit, transactionId]; let conditions = ""; if (after) { queryParams.push(after); - conditions += `\nAND e.id > $3`; + conditions += `\nAND e.id < $3`; } if (before) { queryParams.push(before); - conditions += `\nAND e.id < $3`; + conditions += `\nAND e.id > $3`; } const query = ` @@ -298,6 +328,7 @@ export default class EventDbRepository implements EventRepository { b."chainId" as "chainId", b.height as height, e.module as "moduleName", + e."orderIndex" as "orderIndex", e.name as name, e.params as parameters, b.hash as "blockHash" @@ -306,7 +337,7 @@ export default class EventDbRepository implements EventRepository { join "Blocks" b on b.id = t."blockId" where t.id = $2 ${conditions} - ORDER BY id ${before ? "DESC" : "ASC"} + ORDER BY id ${order} LIMIT $1 `; @@ -317,12 +348,8 @@ export default class EventDbRepository implements EventRepository { node: eventValidator.validate(row), })); - const pageInfo = getPageInfo({ rows: edges, first, last }); - - return { - edges, - pageInfo, - }; + const pageInfo = getPageInfo({ edges, order, limit, after, before }); + return pageInfo; } async getTotalTransactionEventsCount(params: GetTotalTransactionEventsCount) { diff --git a/indexer/src/kadena-server/repository/infra/repository/network-db-repository.ts b/indexer/src/kadena-server/repository/infra/repository/network-db-repository.ts index ebb342f8..72e48d1e 100644 --- a/indexer/src/kadena-server/repository/infra/repository/network-db-repository.ts +++ b/indexer/src/kadena-server/repository/infra/repository/network-db-repository.ts @@ -19,12 +19,49 @@ import { HASH_RATE_AND_TOTAL_DIFFICULTY_KEY, NETWORK_STATISTICS_KEY, } from "../../../../cache/keys"; +import { getCirculationNumber } from "../../../utils/coin-circulation"; const HOST_URL = getRequiredEnvString("NODE_API_URL"); +const SYNC_BASE_URL = getRequiredEnvString("SYNC_BASE_URL"); +const NETWORK_ID = getRequiredEnvString("SYNC_NETWORK"); const NODE_INFO_KEY = "NODE_INFO_KEY"; export default class NetworkDbRepository implements NetworkRepository { + async getCut(): Promise { + const response = await fetch(`${SYNC_BASE_URL}/${NETWORK_ID}/cut`, { + method: "GET", + headers: { + accept: "application/json;charset=utf-8, application/json", + "cache-control": "no-cache", + }, + }); + const data = await response.json(); + + return data.height as number; + } + + async getLatestCreationTime(): Promise { + const creationTimeQuery = ` + SELECT b."creationTime" + FROM "Blocks" b + WHERE b.id = (SELECT max(id) from "Blocks"); + `; + + const { rows } = await rootPgPool.query(creationTimeQuery); + + const latestCreationTime = parseInt(rows[0].creationTime, 10); + + return latestCreationTime; + } + + private async getCoinsInCirculation() { + const cutHeight = await this.getCut(); + const latestCreationTime = await this.getLatestCreationTime(); + const rewards = await getCirculationNumber(cutHeight, latestCreationTime); + return rewards; + } + async getNetworkStatistics() { const totalTransactionsCountQuery = ` SELECT last_value as "totalTransactionsCount" from "Transactions_id_seq" @@ -38,15 +75,7 @@ export default class NetworkDbRepository implements NetworkRepository { 10, ); - const coinsInCirculationQuery = ` - SELECT sum(balance) as "count" - FROM "Balances" - `; - - const { rows: coinsInCirculationRows } = await rootPgPool.query( - coinsInCirculationQuery, - ); - const coinsInCirculation = parseInt(coinsInCirculationRows[0].count, 10); + const coinsInCirculation = await this.getCoinsInCirculation(); const output = { coinsInCirculation, @@ -76,10 +105,10 @@ export default class NetworkDbRepository implements NetworkRepository { const blocksWithDifficulty: BlockWithDifficulty[] = []; for (const block of blocks) { - const timestampInMilliseconds = Number(block.creationTime) / 1000000; // Convert to milliseconds - const creationTime = new Date(timestampInMilliseconds); + const timestampInMilliseconds = Number(block.creationTime); // Convert to milliseconds + const creationTime = new Date(timestampInMilliseconds / 1000); blocksWithDifficulty.push({ - creationTime: timestampInMilliseconds, + creationTime: Number(block.creationTime), creationTimeDate: creationTime, difficulty: calculateBlockDifficulty(block.target), height: BigInt(block.height), diff --git a/indexer/src/kadena-server/repository/infra/repository/transaction-db-repository.ts b/indexer/src/kadena-server/repository/infra/repository/transaction-db-repository.ts index 38322f8c..0630cbda 100644 --- a/indexer/src/kadena-server/repository/infra/repository/transaction-db-repository.ts +++ b/indexer/src/kadena-server/repository/infra/repository/transaction-db-repository.ts @@ -6,7 +6,7 @@ import TransactionRepository, { GetTransactionsParams, TransactionOutput, } from "../../application/transaction-repository"; -import { getPageInfo } from "../../pagination"; +import { getPageInfo, getPaginationParams } from "../../pagination"; import { transactionMetaValidator } from "../schema-validator/transaction-meta-schema-validator"; import { transactionValidator } from "../schema-validator/transaction-schema-validator"; import { signerMetaValidator } from "../schema-validator/signer-schema-validator"; @@ -18,132 +18,232 @@ const operator = (paramsLength: number) => paramsLength > 2 ? `\nAND` : "WHERE"; export default class TransactionDbRepository implements TransactionRepository { - async getTransactions(params: GetTransactionsParams) { - const { - blockHash, - after, - before, - accountName, - chainId, - first, - last, - fungibleName, - requestKey, - maxHeight, - minHeight, - minimumDepth, - hasTokenId = false, - } = params; - - const queryParams: (string | number)[] = [before ? last : first]; - let conditions = ""; + private createBlockConditions( + params: GetTransactionsParams, + queryParams: Array, + ) { + const { blockHash, chainId, maxHeight, minHeight, minimumDepth } = params; + let blocksConditions = ""; + const blockParams: (string | number)[] = [...queryParams]; - if (accountName) { - queryParams.push(accountName); - const op = operator(queryParams.length); - conditions += `${op} t.sender = $${queryParams.length}`; + if (blockHash) { + blockParams.push(blockHash); + const op = operator(blockParams.length); + blocksConditions += `${op} b.hash = $${blockParams.length}`; } - if (blockHash) { - queryParams.push(blockHash); - const op = operator(queryParams.length); - conditions += `${op} b.hash = $${queryParams.length}`; + if (chainId) { + blockParams.push(chainId); + const op = operator(blockParams.length); + blocksConditions += `${op} b."chainId" = $${blockParams.length}`; } - if (after) { - queryParams.push(after); - const op = operator(queryParams.length); - conditions += `${op} t.id > $${queryParams.length}`; + if (maxHeight) { + blockParams.push(maxHeight); + const op = operator(blockParams.length); + blocksConditions += `${op} b."height" <= $${blockParams.length}`; } - if (before) { - queryParams.push(before); - const op = operator(queryParams.length); - conditions += `${op} t.id < $${queryParams.length}`; + if (minHeight) { + blockParams.push(minHeight); + const op = operator(blockParams.length); + blocksConditions += `${op} b."height" >= $${blockParams.length}`; } - if (chainId) { - queryParams.push(chainId); - const op = operator(queryParams.length); - conditions += `${op} b."chainId" = $${queryParams.length}`; + if (minimumDepth) { + blockParams.push(minimumDepth); + const op = operator(blockParams.length); + blocksConditions += `${op} b."minimumDepth" >= $${blockParams.length}`; } - if (requestKey) { - queryParams.push(requestKey); - const op = operator(queryParams.length); - conditions += `${op} t."requestkey" = $${queryParams.length}`; + return { blocksConditions, blockParams }; + } + + private createTransactionConditions( + params: GetTransactionsParams, + queryParams: Array, + ) { + const { + accountName, + after, + before, + requestKey, + fungibleName, + hasTokenId = false, + } = params; + let conditions = ""; + const transactionParams: (string | number)[] = [...queryParams]; + if (accountName) { + transactionParams.push(accountName); + const op = operator(transactionParams.length); + conditions += `${op} t.sender = $${transactionParams.length}`; } - if (maxHeight) { - queryParams.push(maxHeight); - const op = operator(queryParams.length); - conditions += `${op} b."height" <= $${queryParams.length}`; + if (after) { + transactionParams.push(after); + const op = operator(transactionParams.length); + conditions += `${op} t.id < $${transactionParams.length}`; } - if (minHeight) { - queryParams.push(minHeight); - const op = operator(queryParams.length); - conditions += `${op} b."height" >= $${queryParams.length}`; + if (before) { + transactionParams.push(before); + const op = operator(transactionParams.length); + conditions += `${op} t.id > $${transactionParams.length}`; } - if (minimumDepth) { - queryParams.push(minimumDepth); - const op = operator(queryParams.length); - conditions += `${op} b."minimumDepth" >= $${queryParams.length}`; + if (requestKey) { + transactionParams.push(requestKey); + const op = operator(transactionParams.length); + conditions += `${op} t."requestkey" = $${transactionParams.length}`; } if (fungibleName) { - queryParams.push(fungibleName); - const op = operator(queryParams.length); + transactionParams.push(fungibleName); + const op = operator(transactionParams.length); conditions += ` ${op} EXISTS ( SELECT 1 FROM "Events" e WHERE e."transactionId" = t.id - AND e."module" = $${queryParams.length} + AND e."module" = $${transactionParams.length} )`; } if (accountName && hasTokenId) { - queryParams.push(accountName); - const op = operator(queryParams.length + 1); + transactionParams.push(accountName); + const op = operator(transactionParams.length + 1); conditions += ` ${op} EXISTS ( SELECT 1 FROM "Transfers" t - WHERE t."from_acct" = $${queryParams.length} + WHERE t."from_acct" = $${transactionParams.length} AND t."hasTokenId" = true )`; } - const query = ` - SELECT t.id as id, - t.hash as "hashTransaction", - t.nonce as "nonceTransaction", - t.sigs as sigs, - t.continuation as continuation, - t.num_events as "eventCount", - t.pactid as "pactId", - t.proof as proof, - t.rollback as rollback, - b.height as "height", - b."hash" as "blockHash", - b."chainId" as "chainId", - t.gas as "gas", - t.step as step, - t.data as data, - t.code as code, - t.logs as "logs", - t.result as "result", - t.requestkey as "requestKey" - FROM "Blocks" b - JOIN "Transactions" t on b.id = t."blockId" - ${conditions} - ORDER BY t.id ${before ? "DESC" : "ASC"} - LIMIT $1; - `; + return { conditions, params: transactionParams }; + } + + async getTransactions(params: GetTransactionsParams) { + const { + blockHash, + after: afterEncoded, + before: beforeEncoded, + chainId, + first, + last, + maxHeight, + minHeight, + minimumDepth, + } = params; + + const { limit, order, after, before } = getPaginationParams({ + after: afterEncoded, + before: beforeEncoded, + first, + last, + }); + const isBlockQueryFirst = + blockHash || minHeight || maxHeight || minimumDepth || chainId; + + const queryParams: (string | number)[] = []; + let blocksConditions = ""; + let transactionsConditions = ""; + if (isBlockQueryFirst) { + const { blockParams, blocksConditions: bConditions } = + this.createBlockConditions(params, [limit]); + + const { params: txParams, conditions: txConditions } = + this.createTransactionConditions(params, blockParams); + + queryParams.push(...txParams); + transactionsConditions = txConditions; + blocksConditions = bConditions; + } else { + const { conditions, params: txParams } = this.createTransactionConditions( + params, + [limit], + ); + const { blocksConditions: bConditions, blockParams } = + this.createBlockConditions(params, txParams); + + queryParams.push(...blockParams); + transactionsConditions = conditions; + blocksConditions = bConditions; + } + + let query = ""; + if (isBlockQueryFirst) { + query = ` + WITH filtered_block AS ( + SELECT b.id, b.hash, b."chainId", b.height + FROM "Blocks" b + ${blocksConditions} + ) + SELECT + t.id AS id, + t.hash AS "hashTransaction", + t.nonce AS "nonceTransaction", + t.sigs AS sigs, + t.continuation AS continuation, + t.num_events AS "eventCount", + t.pactid AS "pactId", + t.proof AS proof, + t.rollback AS rollback, + t.txid AS txid, + b.height AS "height", + b."hash" AS "blockHash", + b."chainId" AS "chainId", + t.gas AS "gas", + t.step AS step, + t.data AS data, + t.code AS code, + t.logs AS "logs", + t.result AS "result", + t.requestkey AS "requestKey" + FROM filtered_block b + JOIN "Transactions" t ON b.id = t."blockId" + ${transactionsConditions} + ORDER BY t.id ${order} + LIMIT $1 + `; + } else { + query = ` + WITH filtered_transactions AS ( + SELECT id, "blockId", hash, nonce, sigs, continuation, num_events, pactid, proof, rollback, gas, step, data, code, logs, result, requestkey, "chainId", txid + FROM "Transactions" t + ${transactionsConditions} + ORDER BY t.id ${order} + LIMIT $1 + ) + SELECT + t.id AS id, + t.hash AS "hashTransaction", + t.nonce AS "nonceTransaction", + t.sigs AS sigs, + t.continuation AS continuation, + t.num_events AS "eventCount", + t.pactid AS "pactId", + t.proof AS proof, + t.rollback AS rollback, + t.txid AS txid, + b.height AS "height", + b."hash" AS "blockHash", + b."chainId" AS "chainId", + t.gas AS "gas", + t.step AS step, + t.data AS data, + t.code AS code, + t.logs AS "logs", + t.result AS "result", + t.requestkey AS "requestKey" + FROM filtered_transactions t + JOIN "Blocks" b ON b.id = t."blockId" + ${blocksConditions} + `; + } const { rows } = await rootPgPool.query(query, queryParams); @@ -152,12 +252,8 @@ export default class TransactionDbRepository implements TransactionRepository { node: transactionValidator.validate(row), })); - const pageInfo = getPageInfo({ rows: edges, first, last }); - - return { - edges, - pageInfo, - }; + const pageInfo = getPageInfo({ edges, order, limit, after, before }); + return pageInfo; } async getTransactionByTransferId(transferId: string) { @@ -171,6 +267,7 @@ export default class TransactionDbRepository implements TransactionRepository { t.pactid as "pactId", t.proof as proof, t.rollback as rollback, + t.txid AS txid, b.height as "height", b."hash" as "blockHash", b."chainId" as "chainId", @@ -244,6 +341,7 @@ export default class TransactionDbRepository implements TransactionRepository { t.pactid as "pactId", t.proof as proof, t.rollback as rollback, + t.txid AS txid, b.height as "height", b."hash" as "blockHash", b."chainId" as "chainId", @@ -256,9 +354,9 @@ export default class TransactionDbRepository implements TransactionRepository { t.requestkey as "requestKey" FROM "Transactions" t JOIN "Blocks" b on t."blockId" = b.id - WHERE t.requestkey::text = $1 + WHERE t.requestkey = $1 ${conditions} - `; + `; const { rows } = await rootPgPool.query(query, queryParams); @@ -270,21 +368,27 @@ export default class TransactionDbRepository implements TransactionRepository { async getTransactionsByPublicKey({ publicKey, first, - before, - after, + before: beforeEncoded, + after: afterEncoded, last, }: GetTransactionsByPublicKeyParams) { - const queryParams: (string | number)[] = [before ? last : first, publicKey]; + const { limit, order, after, before } = getPaginationParams({ + after: afterEncoded, + before: beforeEncoded, + first, + last, + }); + const queryParams: (string | number)[] = [limit, publicKey]; let cursorCondition = ""; if (after) { - cursorCondition = `\nAND t.id > $3`; + cursorCondition = `\nAND t.id < $3`; queryParams.push(after); } if (before) { - cursorCondition = `\nAND t.id < $3`; + cursorCondition = `\nAND t.id > $3`; queryParams.push(before); } @@ -299,6 +403,7 @@ export default class TransactionDbRepository implements TransactionRepository { t.pactid as "pactId", t.proof as proof, t.rollback as rollback, + t.txid AS txid, b.height as "height", b."hash" as "blockHash", b."chainId" as "chainId", @@ -317,7 +422,7 @@ export default class TransactionDbRepository implements TransactionRepository { WHERE s."pubkey" = $2 ) filtered_signers ON t.id = filtered_signers."transactionId" ${cursorCondition} - ORDER BY t.id ${before ? "DESC" : "ASC"} + ORDER BY t.id ${order} LIMIT $1; `; @@ -328,19 +433,18 @@ export default class TransactionDbRepository implements TransactionRepository { node: transactionValidator.validate(row), })); - const pageInfo = getPageInfo({ rows: edges, first, last }); - - return { - edges, - pageInfo, - }; + const pageInfo = getPageInfo({ edges, order, limit, after, before }); + return pageInfo; } async getTransactionsByPublicKeyCount(publicKey: string) { const query = ` - SELECT COUNT(DISTINCT s."transactionId") as count - FROM "Signers" s - WHERE s.pubkey = $1; + SELECT COUNT(*) as count + FROM ( + SELECT DISTINCT s."transactionId" + FROM "Signers" s + WHERE s.pubkey = $1 + ) subquery; `; const { rows } = await rootPgPool.query(query, [publicKey]); @@ -369,80 +473,102 @@ export default class TransactionDbRepository implements TransactionRepository { minHeight, maxHeight, minimumDepth, + hasTokenId, } = params; - const queryParams: (string | number)[] = []; - let conditions = ""; + const transactionsParams: (string | number)[] = []; + const blockParams: (string | number)[] = []; + let transactionsConditions = ""; + let blocksConditions = ""; const localOperator = (paramsLength: number) => paramsLength > 1 ? `\nAND` : "WHERE"; if (accountName) { - queryParams.push(accountName); - const op = localOperator(queryParams.length); - conditions += `${op} t.sender = $${queryParams.length}`; + transactionsParams.push(accountName); + const op = localOperator(transactionsParams.length); + transactionsConditions += `${op} t.sender = $${transactionsParams.length}`; } - if (blockHash) { - queryParams.push(blockHash); - const op = localOperator(queryParams.length); - conditions += `${op} b.hash = $${queryParams.length}`; + if (requestKey) { + transactionsParams.push(requestKey); + const op = localOperator(transactionsParams.length); + transactionsConditions += `${op} t."requestkey" = $${transactionsParams.length}`; } - if (chainId) { - queryParams.push(chainId); - const op = localOperator(queryParams.length); - conditions += `${op} b."chainId" = $${queryParams.length}`; + if (fungibleName) { + transactionsParams.push(fungibleName); + const op = localOperator(transactionsParams.length); + transactionsConditions += ` + ${op} EXISTS + ( + SELECT 1 + FROM "Events" e + WHERE e."transactionId" = t.id + AND e."module" = $${transactionsParams.length} + )`; } - if (requestKey) { - queryParams.push(requestKey); - const op = localOperator(queryParams.length); - conditions += `${op} t."requestkey" = $${queryParams.length}`; + if (accountName && hasTokenId) { + transactionsParams.push(accountName); + const op = localOperator(transactionsParams.length); + transactionsConditions += ` + ${op} EXISTS + ( + SELECT 1 + FROM "Transfers" t + WHERE t."from_acct" = $${transactionsParams.length} + AND t."hasTokenId" = true + )`; + } + + const paramsOffset = transactionsParams.length; + if (blockHash) { + blockParams.push(blockHash); + const op = localOperator(blockParams.length); + blocksConditions += `${op} b.hash = $${paramsOffset + blockParams.length}`; + } + + if (chainId) { + blockParams.push(chainId); + const op = localOperator(blockParams.length); + blocksConditions += `${op} b."chainId" = $${paramsOffset + blockParams.length}`; } if (maxHeight) { - queryParams.push(maxHeight); - const op = localOperator(queryParams.length); - conditions += `${op} b."height" <= $${queryParams.length}`; + blockParams.push(maxHeight); + const op = localOperator(blockParams.length); + blocksConditions += `${op} b."height" <= $${paramsOffset + blockParams.length}`; } if (minHeight) { - queryParams.push(minHeight); - const op = localOperator(queryParams.length); - conditions += `${op} b."height" >= $${queryParams.length}`; + blockParams.push(minHeight); + const op = localOperator(blockParams.length); + blocksConditions += `${op} b."height" >= $${paramsOffset + blockParams.length}`; } if (minimumDepth) { - queryParams.push(minimumDepth); - const op = localOperator(queryParams.length); - conditions += `${op} b."minimumDepth" >= $${queryParams.length}`; - } - - if (fungibleName) { - queryParams.push(fungibleName); - const op = localOperator(queryParams.length); - conditions += ` - ${op} EXISTS - ( - SELECT 1 - FROM "Events" e - WHERE e."transactionId" = t.id - AND e."module" = $${queryParams.length} - )`; + blockParams.push(minimumDepth); + const op = localOperator(blockParams.length); + blocksConditions += `${op} b."minimumDepth" >= $${paramsOffset + blockParams.length}`; } const totalCountQuery = ` + WITH filtered_transactions AS ( + SELECT id, "blockId" + FROM "Transactions" t + ${transactionsConditions} + ) SELECT COUNT(*) as count - FROM "Blocks" b - JOIN "Transactions" t ON b.id = t."blockId" - ${conditions} + FROM filtered_transactions t + ${blocksConditions ? `JOIN "Blocks" b ON b.id = t."blockId"` : ""} + ${blocksConditions} `; - const { rows: countResult } = await rootPgPool.query( - totalCountQuery, - queryParams, - ); + const { rows: countResult } = await rootPgPool.query(totalCountQuery, [ + ...transactionsParams, + ...blockParams, + ]); const totalCount = parseInt(countResult[0].count, 10); return totalCount; @@ -463,6 +589,7 @@ export default class TransactionDbRepository implements TransactionRepository { t.pactid as "pactId", t.proof as proof, t.rollback as rollback, + t.txid AS txid, b.height as "height", b."hash" as "blockHash", b."chainId" as "chainId", diff --git a/indexer/src/kadena-server/repository/infra/repository/transfer-db-repository.ts b/indexer/src/kadena-server/repository/infra/repository/transfer-db-repository.ts index d703c620..74708faa 100644 --- a/indexer/src/kadena-server/repository/infra/repository/transfer-db-repository.ts +++ b/indexer/src/kadena-server/repository/infra/repository/transfer-db-repository.ts @@ -5,7 +5,7 @@ import TransferRepository, { GetTransfersByTransactionIdParams, GetTransfersParams, } from "../../application/transfer-repository"; -import { getPageInfo } from "../../pagination"; +import { getPageInfo, getPaginationParams } from "../../pagination"; import { transferSchemaValidator } from "../schema-validator/transfer-schema-validator"; const operator = (paramsLength: number) => (paramsLength > 2 ? `AND` : "WHERE"); @@ -14,8 +14,8 @@ export default class TransferDbRepository implements TransferRepository { async getTransfers(params: GetTransfersParams) { const { blockHash, - after, - before, + after: afterEncoded, + before: beforeEncoded, first, last, chainId, @@ -26,31 +26,31 @@ export default class TransferDbRepository implements TransferRepository { moduleHash, } = params; - const queryParams: (string | number)[] = [before ? last : first]; + const { limit, order, after, before } = getPaginationParams({ + after: afterEncoded, + before: beforeEncoded, + first, + last, + }); + const queryParams: (string | number)[] = [limit]; let conditions = ""; if (accountName) { queryParams.push(accountName); const op = operator(queryParams.length); - conditions += `\n${op} transfers.from_acct = $${queryParams.length}`; - } - - if (blockHash) { - queryParams.push(blockHash); - const op = operator(queryParams.length); - conditions += `\n${op} b.hash = $${queryParams.length}`; + conditions += `\n${op} (transfers.from_acct = $${queryParams.length} OR transfers.to_acct = $${queryParams.length})`; } if (after) { queryParams.push(after); const op = operator(queryParams.length); - conditions += `\n${op} transfers.id > $${queryParams.length}`; + conditions += `\n${op} transfers.id < $${queryParams.length}`; } if (before) { queryParams.push(before); const op = operator(queryParams.length); - conditions += `\n${op} transfers.id < $${queryParams.length}`; + conditions += `\n${op} transfers.id > $${queryParams.length}`; } if (chainId) { @@ -65,45 +65,135 @@ export default class TransferDbRepository implements TransferRepository { conditions += `\n${op} transfers."modulename" = $${queryParams.length}`; } - if (requestKey) { - queryParams.push(requestKey); + if (orderIndex) { + queryParams.push(orderIndex); const op = operator(queryParams.length); - conditions += `\n${op} transfers.requestkey = $${queryParams.length}`; + conditions += `\n${op} transfers."orderIndex" = $${queryParams.length}`; } - // if (orderIndex) { // TODO(STREAMING) - // queryParams.push(orderIndex); - // const op = operator(queryParams.length); - // conditions += `\n${op} transfers."orderIndex" = $${queryParams.length}`; - // } - if (moduleHash) { queryParams.push(moduleHash); const op = operator(queryParams.length); conditions += `\n${op} transfers.modulehash = $${queryParams.length}`; } - const query = ` - select transfers.id as id, - transfers.amount as "transferAmount", - transactions."chainId" as "chainId", - transactions."creationtime" as "creationTime", - transactions.id as "transactionId", - b.height as "height", - b.hash as "blockHash", - transfers."from_acct" as "senderAccount", - transfers."to_acct" as "receiverAccount", - transfers.modulename as "moduleName", - transfers.modulehash as "moduleHash", - transfers.requestkey as "requestKey", - transactions.pactid as "pactId" - from "Blocks" b - join "Transactions" transactions on b.id = transactions."blockId" - join "Transfers" transfers on transfers."transactionId" = transactions.id - ${conditions} - ORDER BY transfers.id ${before ? "DESC" : "ASC"} - LIMIT $1; - `; + let query = ""; + + if (blockHash) { + queryParams.push(blockHash); + const op = operator(queryParams.length); + query = ` + WITH filtered_block AS ( + SELECT id, height, hash + FROM "Blocks" b + ${op} b.hash = $${queryParams.length} + ) + select transfers.id as id, + transfers.amount as "transferAmount", + t."chainId" as "chainId", + t."creationtime" as "creationTime", + t.id as "transactionId", + b.height as "height", + b.hash as "blockHash", + transfers."from_acct" as "senderAccount", + transfers."to_acct" as "receiverAccount", + transfers.modulename as "moduleName", + transfers.modulehash as "moduleHash", + transfers.requestkey as "requestKey", + transfers."orderIndex" as "orderIndex", + t.pactid as "pactId" + from filtered_block b + join "Transactions" t on b.id = t."blockId" + join "Transfers" transfers on transfers."transactionId" = t.id + ${conditions} + ORDER BY transfers.id ${order} + LIMIT $1 + `; + } else if (requestKey) { + queryParams.push(requestKey); + query = ` + WITH filtered_transaction AS ( + SELECT t.id, t.pactid, t."chainId", t."creationtime", t."blockId" + FROM "Transactions" t + WHERE t.requestkey = $${queryParams.length} + ) + select transfers.id as id, + transfers.amount as "transferAmount", + t."chainId" as "chainId", + t."creationtime" as "creationTime", + t.id as "transactionId", + b.height as "height", + b.hash as "blockHash", + transfers."from_acct" as "senderAccount", + transfers."to_acct" as "receiverAccount", + transfers.modulename as "moduleName", + transfers.modulehash as "moduleHash", + transfers.requestkey as "requestKey", + transfers."orderIndex" as "orderIndex", + t.pactid as "pactId" + from filtered_transaction t + join "Blocks" b on b.id = t."blockId" + join "Transfers" transfers on transfers."transactionId" = t.id + ${conditions} + ORDER BY transfers.id ${order} + LIMIT $1 + `; + } else if (accountName) { + query = ` + WITH filtered_transfers AS ( + SELECT id, amount, "transactionId", "from_acct", "to_acct", modulename, modulehash, requestkey, "orderIndex" + FROM "Transfers" transfers + ${conditions} + ORDER BY transfers.id ${order} + LIMIT ALL + ) + select transfers.id as id, + transfers.amount as "transferAmount", + t."chainId" as "chainId", + t."creationtime" as "creationTime", + t.id as "transactionId", + b.height as "height", + b.hash as "blockHash", + transfers."from_acct" as "senderAccount", + transfers."to_acct" as "receiverAccount", + transfers.modulename as "moduleName", + transfers.modulehash as "moduleHash", + transfers.requestkey as "requestKey", + transfers."orderIndex" as "orderIndex", + t.pactid as "pactId" + from filtered_transfers transfers + join "Transactions" t on t.id = transfers."transactionId" + join "Blocks" b on b."id" = t."blockId" + LIMIT $1 + `; + } else { + query = ` + WITH filtered_transfers AS ( + SELECT id, amount, "transactionId", "from_acct", "to_acct", modulename, modulehash, requestkey, "orderIndex" + FROM "Transfers" transfers + ${conditions} + ORDER BY transfers.id ${order} + LIMIT $1 + ) + select transfers.id as id, + transfers.amount as "transferAmount", + t."chainId" as "chainId", + t."creationtime" as "creationTime", + t.id as "transactionId", + b.height as "height", + b.hash as "blockHash", + transfers."from_acct" as "senderAccount", + transfers."to_acct" as "receiverAccount", + transfers.modulename as "moduleName", + transfers.modulehash as "moduleHash", + transfers.requestkey as "requestKey", + transfers."orderIndex" as "orderIndex", + t.pactid as "pactId" + from filtered_transfers transfers + join "Transactions" t on t.id = transfers."transactionId" + join "Blocks" b on b."id" = t."blockId" + `; + } const { rows } = await rootPgPool.query(query, queryParams); @@ -112,12 +202,8 @@ export default class TransferDbRepository implements TransferRepository { node: transferSchemaValidator.validate(row), })); - const pageInfo = getPageInfo({ rows: edges, first, last }); - - return { - edges, - pageInfo, - }; + const pageInfo = getPageInfo({ edges, order, limit, after, before }); + return pageInfo; } async getCrossChainTransferByPactId({ @@ -137,11 +223,12 @@ export default class TransferDbRepository implements TransferRepository { transfers.modulename as "moduleName", transfers.modulehash as "moduleHash", transfers.requestkey as "requestKey", + transfers."orderIndex" as "orderIndex", transactions.pactid as "pactId" from "Blocks" b join "Transactions" transactions on b.id = transactions."blockId" join "Transfers" transfers on transfers."transactionId" = transactions.id - where transactions.requestKey = $1 + where transactions.requestkey = $1 and transfers.amount = $2 `; @@ -153,6 +240,17 @@ export default class TransferDbRepository implements TransferRepository { } async getTotalCountOfTransfers(params: GetTotalCountParams): Promise { + const hasNoParams = Object.values(params).every((v) => !v); + + if (hasNoParams) { + const totalTransfersCountQuery = ` + SELECT last_value as "totalTransfersCount" from "Transfers_id_seq" + `; + const { rows } = await rootPgPool.query(totalTransfersCountQuery); + const transfersCount = parseInt(rows[0].totalTransfersCount, 10); + return transfersCount; + } + const { blockHash, accountName, @@ -169,7 +267,7 @@ export default class TransferDbRepository implements TransferRepository { if (accountName) { queryParams.push(accountName); const op = localOperator(queryParams.length); - conditions += `${op} trans.from_acct = $${queryParams.length}`; + conditions += `\n${op} (trans.from_acct = $${queryParams.length} OR trans.to_acct = $${queryParams.length})`; } if (blockHash) { @@ -199,7 +297,7 @@ export default class TransferDbRepository implements TransferRepository { if (requestKey) { queryParams.push(requestKey); const op = localOperator(queryParams.length); - conditions += `${op} transactions."requestKey" = $${queryParams.length}`; + conditions += `${op} t.requestkey = $${queryParams.length}`; } const totalCountQuery = ` @@ -220,22 +318,32 @@ export default class TransferDbRepository implements TransferRepository { } async getTransfersByTransactionId(params: GetTransfersByTransactionIdParams) { - const { transactionId, after, before, first, last } = params; - - const queryParams: (string | number)[] = [ - before ? last : first, + const { transactionId, - ]; + after: afterEncoded, + before: beforeEncoded, + first, + last, + } = params; + + const { limit, order, after, before } = getPaginationParams({ + after: afterEncoded, + before: beforeEncoded, + first, + last, + }); + + const queryParams: (string | number)[] = [limit, transactionId]; let conditions = ""; if (before) { queryParams.push(before); - conditions += `\nAND transfers.id < $3`; + conditions += `\nAND transfers.id > $3`; } if (after) { queryParams.push(after); - conditions += `\nAND transfers.id > $3`; + conditions += `\nAND transfers.id < $3`; } const query = ` @@ -250,13 +358,14 @@ export default class TransferDbRepository implements TransferRepository { transfers.modulename as "moduleName", transfers.modulehash as "moduleHash", transfers.requestkey as "requestKey", + transfers."orderIndex" as "orderIndex", transactions.pactid as "pactId" from "Blocks" b join "Transactions" transactions on b.id = transactions."blockId" join "Transfers" transfers on transfers."transactionId" = transactions.id WHERE transactions.id = $2 ${conditions} - ORDER BY transfers.id ${before ? "DESC" : "ASC"} + ORDER BY transfers.id ${order} LIMIT $1; `; @@ -267,11 +376,7 @@ export default class TransferDbRepository implements TransferRepository { node: transferSchemaValidator.validate(row), })); - const pageInfo = getPageInfo({ rows: edges, first, last }); - - return { - edges, - pageInfo, - }; + const pageInfo = getPageInfo({ edges, order, limit, after, before }); + return pageInfo; } } diff --git a/indexer/src/kadena-server/repository/infra/schema-validator/block-schema-validator.ts b/indexer/src/kadena-server/repository/infra/schema-validator/block-schema-validator.ts index 9bf0317d..061d2a1a 100644 --- a/indexer/src/kadena-server/repository/infra/schema-validator/block-schema-validator.ts +++ b/indexer/src/kadena-server/repository/infra/schema-validator/block-schema-validator.ts @@ -43,7 +43,7 @@ const validate = (row: any): BlockOutput => { target: res.target, weight: res.weight, chainId: res.chainId, - difficulty: calculateBlockDifficulty(res.target).toString(), + difficulty: Number(calculateBlockDifficulty(res.target)), neighbors: Object.entries(res.adjacents).map(([chainId, hash]) => ({ chainId, hash, @@ -51,8 +51,6 @@ const validate = (row: any): BlockOutput => { }; }; -const JSONbig = require("json-bigint"); - const mapFromSequelize = (blockModel: BlockAttributes): BlockOutput => { return { id: getBase64ID(blockModel.hash), @@ -61,7 +59,7 @@ const mapFromSequelize = (blockModel: BlockAttributes): BlockOutput => { chainId: blockModel.chainId, creationTime: convertStringToDate(blockModel.creationTime), powHash: "...", // TODO (STREAMING) - difficulty: JSONbig.parse(calculateBlockDifficulty(blockModel.target)), + difficulty: Number(calculateBlockDifficulty(blockModel.target)), epoch: convertStringToDate(blockModel.epochStart), flags: int64ToUint64String(blockModel.featureFlags), height: blockModel.height, diff --git a/indexer/src/kadena-server/repository/infra/schema-validator/event-schema-validator.ts b/indexer/src/kadena-server/repository/infra/schema-validator/event-schema-validator.ts index 889c59e2..f286dee0 100644 --- a/indexer/src/kadena-server/repository/infra/schema-validator/event-schema-validator.ts +++ b/indexer/src/kadena-server/repository/infra/schema-validator/event-schema-validator.ts @@ -7,6 +7,7 @@ const schema = zod.object({ blockHash: zod.string(), requestKey: zod.string(), chainId: zod.number(), + orderIndex: zod.number().nullable(), moduleName: zod.string(), height: zod.number(), parameters: zod.array(zod.any()), @@ -25,9 +26,9 @@ const getBase64ID = ( function validate(row: any): EventOutput { const res = schema.parse(row); return { - id: getBase64ID(res.blockHash, 0, res.requestKey), + id: getBase64ID(res.blockHash, res.orderIndex ?? 0, res.requestKey), eventId: res.id.toString(), - orderIndex: 0, // TODO (STREAMING) + orderIndex: res.orderIndex ?? 0, name: res.name, requestKey: res.requestKey, chainId: res.chainId, @@ -35,6 +36,7 @@ function validate(row: any): EventOutput { height: res.height, qualifiedName: `${res.moduleName}.${res.name}`, parameters: JSON.stringify(res.parameters), + parameterText: JSON.stringify(res.parameters), }; } diff --git a/indexer/src/kadena-server/repository/infra/schema-validator/signer-schema-validator.ts b/indexer/src/kadena-server/repository/infra/schema-validator/signer-schema-validator.ts index 13dc7f25..48e2bd50 100644 --- a/indexer/src/kadena-server/repository/infra/schema-validator/signer-schema-validator.ts +++ b/indexer/src/kadena-server/repository/infra/schema-validator/signer-schema-validator.ts @@ -29,7 +29,7 @@ function validate(row: any): SignerOutput { orderIndex: res.signerOrderIndex, scheme: "", clist: (res.clist ?? []).map((c) => ({ - args: c.args.toString(), + args: JSON.stringify(c.args), name: c.name, })), }; diff --git a/indexer/src/kadena-server/repository/infra/schema-validator/transaction-meta-schema-validator.ts b/indexer/src/kadena-server/repository/infra/schema-validator/transaction-meta-schema-validator.ts index 94093450..a960eb34 100644 --- a/indexer/src/kadena-server/repository/infra/schema-validator/transaction-meta-schema-validator.ts +++ b/indexer/src/kadena-server/repository/infra/schema-validator/transaction-meta-schema-validator.ts @@ -1,6 +1,5 @@ import zod from "zod"; import { TransactionMetaOutput } from "../../application/transaction-repository"; -import { convertStringToDate } from "../../../utils/date"; const schema = zod.object({ chainId: zod.number(), @@ -15,7 +14,7 @@ function validate(row: any): TransactionMetaOutput { const res = schema.parse(row); return { chainId: res.chainId, - creationTime: convertStringToDate(res.creationTime), + creationTime: new Date(Number(res.creationTime) * 1000), gasLimit: res.gasLimit, gasPrice: Number(res.gasPrice), sender: res.sender, diff --git a/indexer/src/kadena-server/repository/infra/schema-validator/transaction-schema-validator.ts b/indexer/src/kadena-server/repository/infra/schema-validator/transaction-schema-validator.ts index 00153675..a10225dd 100644 --- a/indexer/src/kadena-server/repository/infra/schema-validator/transaction-schema-validator.ts +++ b/indexer/src/kadena-server/repository/infra/schema-validator/transaction-schema-validator.ts @@ -1,6 +1,9 @@ +import { getRequiredEnvString } from "../../../../utils/helpers"; import { TransactionOutput } from "../../application/transaction-repository"; import zod from "zod"; +const NETWORK_ID = getRequiredEnvString("SYNC_NETWORK"); + const getBase64ID = (blockHash: string, requestKey: string): string => { const inputString = `Transaction:[\"${blockHash}\",\"${requestKey}\"]`; const base64ID = Buffer.from(inputString, "utf-8").toString("base64"); @@ -10,6 +13,7 @@ const getBase64ID = (blockHash: string, requestKey: string): string => { const schema = zod.object({ id: zod.number(), hashTransaction: zod.string(), + txid: zod.string().nullable(), sigs: zod.array(zod.any()), continuation: zod.any(), eventCount: zod.number(), @@ -26,15 +30,15 @@ const schema = zod.object({ blockHash: zod.string(), requestKey: zod.string(), result: zod.any(), - // networkId: zod.string(), TODO (STREAMING) }); function validate(row: any): TransactionOutput { const res = schema.parse(row); const isSuccess = res.result.status === "success"; + const continuation = JSON.stringify(res.continuation); return { id: getBase64ID(res.blockHash, res.requestKey), - transactionId: res.id.toString(), + databaseTransactionId: res.id.toString(), blockHeight: res.height, blockHash: res.blockHash, hash: res.hashTransaction, @@ -45,18 +49,18 @@ function validate(row: any): TransactionOutput { // TransactionResult badResult: !isSuccess ? res.result.data : null, - continuation: res.continuation.toString(), + continuation: continuation === "{}" ? null : continuation, eventCount: res.eventCount, + transactionId: res.txid ? res.txid : null, gas: res.gas, - goodResult: isSuccess ? res.result.data : null, - height: res.height, + goodResult: isSuccess ? JSON.stringify(res.result.data) : null, logs: res.logs, }, cmd: { payload: { // ExecutionPayload - code: res.code, + code: JSON.stringify(res.code), // ContinuationPayload and ExecutionPayload data: JSON.stringify(res.data), @@ -67,7 +71,7 @@ function validate(row: any): TransactionOutput { rollback: res.rollback, step: res.step, }, - networkId: "mainnet", // TODO (STREAMING) + networkId: NETWORK_ID, nonce: row.nonceTransaction, }, }; diff --git a/indexer/src/kadena-server/repository/infra/schema-validator/transfer-schema-validator.ts b/indexer/src/kadena-server/repository/infra/schema-validator/transfer-schema-validator.ts index cfbd794d..76e8b9a6 100644 --- a/indexer/src/kadena-server/repository/infra/schema-validator/transfer-schema-validator.ts +++ b/indexer/src/kadena-server/repository/infra/schema-validator/transfer-schema-validator.ts @@ -1,6 +1,5 @@ import zod from "zod"; import { TransferOutput } from "../../application/transfer-repository"; -import { convertStringToDate } from "../../../utils/date"; const schema = zod.object({ id: zod.number(), @@ -11,7 +10,7 @@ const schema = zod.object({ height: zod.number(), moduleName: zod.string(), moduleHash: zod.string(), - // orderIndex: zod.number(), + orderIndex: zod.number(), receiverAccount: zod.string(), senderAccount: zod.string(), requestKey: zod.string(), @@ -36,14 +35,19 @@ function validate(row: any): TransferOutput { id: getBase64ID( res.blockHash, res.chainId, - 0, // orderIndex + res.orderIndex, res.moduleHash, res.requestKey, ), + creationTime: new Date(Number(res.creationTime) * 1000), + moduleHash: res.moduleHash, + requestKey: res.requestKey, amount: res.transferAmount, blockHash: res.blockHash, + chainId: res.chainId, + height: res.height, moduleName: res.moduleName, - orderIndex: 0, // orderIndex + orderIndex: res.orderIndex, receiverAccount: res.receiverAccount, senderAccount: res.senderAccount, transferId: res.id.toString(), diff --git a/indexer/src/kadena-server/repository/pagination.ts b/indexer/src/kadena-server/repository/pagination.ts index 502b6994..e4ca832f 100644 --- a/indexer/src/kadena-server/repository/pagination.ts +++ b/indexer/src/kadena-server/repository/pagination.ts @@ -4,23 +4,87 @@ import { ConnectionEdge } from "./types"; export interface PaginationsParams { after?: InputMaybe; before?: InputMaybe; - first: number; - last: number; + first?: InputMaybe; + last?: InputMaybe; } -interface Params { - rows: ConnectionEdge[]; - first: number; - last?: number | null; +const DEFAULT_LIMIT = 20; +const LIMIT_NEXT_PAGE_CHECK = 1; + +interface Params { + order: "ASC" | "DESC"; + limit: number; + edges: ConnectionEdge[]; + after?: string | null; + before?: string | null; } -export const getPageInfo = ({ first, last, rows }: Params): PageInfo => { - const length = rows.length; - const hasNextPage: boolean = Boolean(first && length === first); - const hasPreviousPage: boolean = Boolean(last && length === last); +export const encodeCursor = (cursor: string): string => + Buffer.from(cursor).toString("base64"); + +export const decodeCursor = (cursor: string): string => + Buffer.from(cursor, "base64").toString("utf8"); + +export const getPageInfo = ({ + order = "DESC", + limit: limitParam, + edges, + after, + before, +}: Params): { pageInfo: PageInfo; edges: ConnectionEdge[] } => { + const length = edges.length; + + const limit = (limitParam ?? DEFAULT_LIMIT) - LIMIT_NEXT_PAGE_CHECK; + + if (length === 0) { + return { + pageInfo: { + startCursor: null, + endCursor: null, + hasNextPage: false, + hasPreviousPage: false, + }, + edges, + }; + } + + if (length === 1) { + return { + pageInfo: { + startCursor: encodeCursor(edges[0].cursor), + endCursor: encodeCursor(edges[0].cursor), + hasNextPage: !!before, + hasPreviousPage: !!after, + }, + edges, + }; + } + + let hasNextPage = false; + let hasPreviousPage = false; + let startCursor = null; + let endCursor = null; + let newEdges = null; + const idx = Math.min(length, limit); + if (order === "DESC") { + hasNextPage = length > limit; + hasPreviousPage = !!after; + startCursor = encodeCursor(edges[0].cursor); + endCursor = encodeCursor(edges[idx - 1].cursor); + newEdges = edges.slice(0, idx); + } else { + hasNextPage = !!before; + hasPreviousPage = length > limit; + const reversed = edges.slice(0, idx).reverse(); + startCursor = encodeCursor(reversed[0].cursor); + endCursor = encodeCursor(reversed[reversed.length - 1].cursor); + newEdges = [...reversed]; + } - const startCursor = !!length ? rows[0].cursor.toString() : null; - const endCursor = !!length ? rows[rows.length - 1].cursor.toString() : null; + const edgesWithCursorEncoded = newEdges.map((e) => ({ + cursor: encodeCursor(e.cursor), + node: e.node, + })); const pageInfo = { startCursor, @@ -29,20 +93,68 @@ export const getPageInfo = ({ first, last, rows }: Params): PageInfo => { hasPreviousPage, }; - return pageInfo; + return { pageInfo, edges: edgesWithCursorEncoded }; +}; + +type PaginationInput = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +}; + +type PaginationOutput = { + limit: number; + order: "ASC" | "DESC"; + after: string | null; + before: string | null; }; -export const addPagination = ( - query: string, - queryParams: Array, - after?: InputMaybe, -) => { - let cursorCondition = ""; +export function getPaginationParams({ + after, + before, + first, + last, +}: PaginationInput): PaginationOutput { if (after) { - const decodedAfter = after; // This could just decode to a simple event ID - cursorCondition = `AND t.id > ${after}`; - queryParams.push(decodedAfter); + return { + limit: (first ?? DEFAULT_LIMIT) + LIMIT_NEXT_PAGE_CHECK, + order: "DESC", + after: decodeCursor(after), + before: null, + }; } - return { query, queryParams }; -}; + if (before) { + return { + limit: (last ?? DEFAULT_LIMIT) + LIMIT_NEXT_PAGE_CHECK, + order: "ASC", + after: null, + before: decodeCursor(before), + }; + } + + if (first) { + return { + limit: first + LIMIT_NEXT_PAGE_CHECK, + order: "DESC", + after: null, + before: null, + }; + } + if (last) { + return { + limit: last + LIMIT_NEXT_PAGE_CHECK, + order: "ASC", + after: null, + before: null, + }; + } + + return { + limit: DEFAULT_LIMIT + LIMIT_NEXT_PAGE_CHECK, + order: "DESC", + after: null, + before: null, + }; +} diff --git a/indexer/src/kadena-server/resolvers/fields/transaction-command/meta-transaction-command-resolver.ts b/indexer/src/kadena-server/resolvers/fields/transaction-command/meta-transaction-command-resolver.ts index b307317b..4e25e221 100644 --- a/indexer/src/kadena-server/resolvers/fields/transaction-command/meta-transaction-command-resolver.ts +++ b/indexer/src/kadena-server/resolvers/fields/transaction-command/meta-transaction-command-resolver.ts @@ -2,7 +2,7 @@ import { ResolverContext } from "../../../config/apollo-server-config"; import { TransactionCommandResolvers } from "../../../config/graphql-types"; import zod from "zod"; -const schema = zod.object({ transactionId: zod.string() }); +const schema = zod.object({ databaseTransactionId: zod.string() }); export const metaTransactionCommandResolver: TransactionCommandResolvers["meta"] = async (parent, _args, context) => { @@ -12,7 +12,7 @@ export const metaTransactionCommandResolver: TransactionCommandResolvers["signers"] = async (parent, _args, context) => { @@ -11,7 +11,7 @@ export const signersTransactionCommandResolver: TransactionCommandResolvers["block"] = async (parent, _args, context) => { @@ -12,7 +12,7 @@ export const blockTransactionResultResolver: TransactionResultResolvers["totalCount"] = async (parent, _args, context) => { console.log("totalCountTransactionResultEventsConnectionResolver"); + const { databaseTransactionId } = schema.parse(parent); + const output = await context.eventRepository.getTotalTransactionEventsCount( { - transactionId: parent.transactionId, - } + transactionId: databaseTransactionId, + }, ); return output; }; diff --git a/indexer/src/kadena-server/resolvers/fields/transaction-result/events-transaction-result-resolver.ts b/indexer/src/kadena-server/resolvers/fields/transaction-result/events-transaction-result-resolver.ts index 734fa9c7..f2aaaa91 100644 --- a/indexer/src/kadena-server/resolvers/fields/transaction-result/events-transaction-result-resolver.ts +++ b/indexer/src/kadena-server/resolvers/fields/transaction-result/events-transaction-result-resolver.ts @@ -1,15 +1,20 @@ import { ResolverContext } from "../../../config/apollo-server-config"; import { TransactionResultResolvers } from "../../../config/graphql-types"; import { buildEventOutput } from "../../output/build-event-output"; +import zod from "zod"; + +const schema = zod.object({ databaseTransactionId: zod.string() }); export const eventsTransactionResultResolver: TransactionResultResolvers["events"] = async (parent, args, context) => { - console.log("eventsTransactionResultResolver", parent.transactionId); + console.log("eventsTransactionResultResolver"); + + const parentArgs = schema.parse(parent); const { first, after, before, last } = args; const output = await context.eventRepository.getTransactionEvents({ - transactionId: parent.transactionId, + transactionId: parentArgs.databaseTransactionId, first, after, before, @@ -25,7 +30,7 @@ export const eventsTransactionResultResolver: TransactionResultResolvers["totalCount"] = async (parent, _args, context) => { console.log("totalCountTransactionResultTransfersConnectionResolver"); + const { databaseTransactionId } = schema.parse(parent); + const output = await context.transferRepository.getTotalCountOfTransfers({ - transactionId: parent.transactionId, + transactionId: databaseTransactionId, }); return output; }; diff --git a/indexer/src/kadena-server/resolvers/fields/transaction-result/transfers-transaction-result-tesolver.ts b/indexer/src/kadena-server/resolvers/fields/transaction-result/transfers-transaction-result-tesolver.ts index 87a3be7b..c3384b00 100644 --- a/indexer/src/kadena-server/resolvers/fields/transaction-result/transfers-transaction-result-tesolver.ts +++ b/indexer/src/kadena-server/resolvers/fields/transaction-result/transfers-transaction-result-tesolver.ts @@ -1,16 +1,21 @@ import { ResolverContext } from "../../../config/apollo-server-config"; import { TransactionResultResolvers } from "../../../config/graphql-types"; import { buildTransferOutput } from "../../output/build-transfer-output"; +import zod from "zod"; + +const schema = zod.object({ databaseTransactionId: zod.string() }); export const transfersTransactionResultResolver: TransactionResultResolvers["transfers"] = async (parent, args, context) => { console.log("transfersTransactionResultResolver"); + const parentArgs = schema.parse(parent); + const { first, after, before, last } = args; const output = await context.transferRepository.getTransfersByTransactionId( { - transactionId: parent.transactionId, + transactionId: parentArgs.databaseTransactionId, first, after, before, @@ -27,7 +32,7 @@ export const transfersTransactionResultResolver: TransactionResultResolvers = { DateTime: DateTimeResolver, - BigInt: BigIntResolver, - Decimal: NonNegativeFloatResolver, Subscription: { transaction: transactionSubscriptionResolver, newBlocks: newBlocksSubscriptionResolver, @@ -85,7 +79,7 @@ export const resolvers: Resolvers = { events: eventsQueryResolver, fungibleAccount: fungibleAccountQueryResolver, fungibleAccountsByPublicKey: fungibleAccountsByPublicKeyQueryResolver, - fungibleChainAccount: fungibleChainAccountQueryResolver, + fungibleChainAccounts: fungibleChainAccountsQueryResolver, fungibleChainAccountsByPublicKey: fungibleChainAccountsByPublicKeyQueryResolver, gasLimitEstimate: gasLimitEstimateQueryResolver, @@ -185,7 +179,7 @@ export const resolvers: Resolvers = { }, Node: { __resolveType(obj: any) { - if (obj.difficulty && obj.powHash && obj.chainId) { + if (obj.difficulty && obj.powHash) { return "Block"; } @@ -193,11 +187,11 @@ export const resolvers: Resolvers = { return "Event"; } - if (obj.tokenId && obj.version) { + if (obj.tokenId !== undefined && obj.version) { return "NonFungibleTokenBalance"; } - if (obj.chainId && obj.nonFungibleTokenBalances) { + if (obj.chainId !== undefined && obj.nonFungibleTokenBalances) { return "NonFungibleChainAccount"; } @@ -215,7 +209,7 @@ export const resolvers: Resolvers = { if ( obj.accountName && - obj.chainId && + obj.chainId !== undefined && obj.balance !== undefined && obj.balance !== null ) { @@ -230,7 +224,10 @@ export const resolvers: Resolvers = { return "Transaction"; } - if (obj.senderAccount && obj.receiverAccount) { + if ( + obj.senderAccount !== undefined && + obj.receiverAccount !== undefined + ) { return "Transfer"; } @@ -253,4 +250,9 @@ export const resolvers: Resolvers = { return "ContinuationPayload"; }, }, + IGuard: { + __resolveType: (obj: any) => { + return "KeysetGuard"; + }, + }, }; diff --git a/indexer/src/kadena-server/resolvers/node-utils.ts b/indexer/src/kadena-server/resolvers/node-utils.ts index 1061f63c..632d960d 100644 --- a/indexer/src/kadena-server/resolvers/node-utils.ts +++ b/indexer/src/kadena-server/resolvers/node-utils.ts @@ -146,6 +146,12 @@ export const getNode = async (context: ResolverContext, id: string) => { balance: account.balance, tokenId: account.tokenId, version: nftsInfo.version, + // TODO + guard: { + keys: [], + predicate: "", + raw: JSON.stringify("{}"), + }, info: { precision: nftsInfo.precision, supply: nftsInfo.supply, diff --git a/indexer/src/kadena-server/resolvers/output/build-non-fungible-account-output.ts b/indexer/src/kadena-server/resolvers/output/build-non-fungible-account-output.ts index 4c7c75ff..8ff1cc68 100644 --- a/indexer/src/kadena-server/resolvers/output/build-non-fungible-account-output.ts +++ b/indexer/src/kadena-server/resolvers/output/build-non-fungible-account-output.ts @@ -18,6 +18,12 @@ export const buildNonFungibleAccount = ( balance: acc.nonFungibleTokenBalances[index].balance, tokenId: acc.nonFungibleTokenBalances[index].tokenId, version: nft.version, + // TODO + guard: { + keys: [], + predicate: "", + raw: JSON.stringify("{}"), + }, info: { precision: nft.precision, supply: nft.supply, @@ -29,6 +35,8 @@ export const buildNonFungibleAccount = ( id: acc.id, accountName: acc.accountName, nonFungibleTokenBalances, + // TODO + chainAccounts: [], // for resolvers transactions: {} as NonFungibleAccountTransactionsConnection, }; diff --git a/indexer/src/kadena-server/resolvers/output/build-non-fungible-chain-account-output.ts b/indexer/src/kadena-server/resolvers/output/build-non-fungible-chain-account-output.ts index dfa372f8..48bf0673 100644 --- a/indexer/src/kadena-server/resolvers/output/build-non-fungible-chain-account-output.ts +++ b/indexer/src/kadena-server/resolvers/output/build-non-fungible-chain-account-output.ts @@ -15,6 +15,12 @@ export const buildNonFungibleChainAccount = ( balance: acc.nonFungibleTokenBalances[index].balance, tokenId: acc.nonFungibleTokenBalances[index].tokenId, version: nft.version, + // TODO + guard: { + keys: [], + predicate: "", + raw: JSON.stringify("{}"), + }, info: { precision: nft.precision, supply: nft.supply, diff --git a/indexer/src/kadena-server/resolvers/output/build-transaction-output.ts b/indexer/src/kadena-server/resolvers/output/build-transaction-output.ts index 2410b420..bdc3b9f7 100644 --- a/indexer/src/kadena-server/resolvers/output/build-transaction-output.ts +++ b/indexer/src/kadena-server/resolvers/output/build-transaction-output.ts @@ -7,14 +7,14 @@ export const buildTransactionOutput = (tx: TransactionOutput) => { cmd: { ...tx.cmd, // for resolvers - transactionId: tx.transactionId, + databaseTransactionId: tx.databaseTransactionId, meta: {} as TransactionMeta, signers: [] as Signer[], }, result: { ...tx.result, // for resolvers - transactionId: tx.transactionId, + databaseTransactionId: tx.databaseTransactionId, blockHash: tx.blockHash, }, }; diff --git a/indexer/src/kadena-server/resolvers/query/fungible-chain-account-query-resolver.ts b/indexer/src/kadena-server/resolvers/query/fungible-chain-account-query-resolver.ts index 29ed09ab..96801e8d 100644 --- a/indexer/src/kadena-server/resolvers/query/fungible-chain-account-query-resolver.ts +++ b/indexer/src/kadena-server/resolvers/query/fungible-chain-account-query-resolver.ts @@ -2,10 +2,10 @@ import { ResolverContext } from "../../config/apollo-server-config"; import { QueryResolvers } from "../../config/graphql-types"; import { buildFungibleChainAccount } from "../output/build-fungible-chain-account-output"; -export const fungibleChainAccountQueryResolver: QueryResolvers["fungibleChainAccount"] = +export const fungibleChainAccountsQueryResolver: QueryResolvers["fungibleChainAccounts"] = async (_parent, args, context) => { const { accountName, chainIds, fungibleName } = args; - console.log("fungibleChainAccountQueryResolver"); + console.log("fungibleChainAccountsQueryResolver"); const accounts = await context.balanceRepository.getChainsAccountInfo_NODE( accountName, fungibleName, diff --git a/indexer/src/kadena-server/resolvers/query/graph-configuration-query-resolver.ts b/indexer/src/kadena-server/resolvers/query/graph-configuration-query-resolver.ts index ed46f5ff..68dea98b 100644 --- a/indexer/src/kadena-server/resolvers/query/graph-configuration-query-resolver.ts +++ b/indexer/src/kadena-server/resolvers/query/graph-configuration-query-resolver.ts @@ -10,5 +10,6 @@ export const graphConfigurationQueryResolver: QueryResolvers["g return { minimumBlockHeight, + version: "0.1.0", }; }; diff --git a/indexer/src/kadena-server/resolvers/query/transaction-query-resolver.ts b/indexer/src/kadena-server/resolvers/query/transaction-query-resolver.ts index 8e7c8b5d..f403afd7 100644 --- a/indexer/src/kadena-server/resolvers/query/transaction-query-resolver.ts +++ b/indexer/src/kadena-server/resolvers/query/transaction-query-resolver.ts @@ -14,21 +14,21 @@ export const transactionQueryResolver: QueryResolvers["transact minimumDepth, }); - const lastBlockHeight = await context.blockRepository.getLastBlockHeight(); + // TODO: implement orphaned transactions + // const lastBlockHeight = await context.blockRepository.getLastBlockHeight(); + // const { baselineTransaction, orphanedTransactions } = + // getBaselineAndOrphanedTransactions(output, lastBlockHeight); + // if (!baselineTransaction) return null; + // const orphanedTxs = orphanedTransactions.map((t) => + // buildTransactionOutput(t), + // ); - const { baselineTransaction, orphanedTransactions } = - getBaselineAndOrphanedTransactions(output, lastBlockHeight); + const baseTx = output.length ? buildTransactionOutput(output[0]) : null; - if (!baselineTransaction) return null; - - const baseTx = buildTransactionOutput(baselineTransaction); - - const orphanedTxs = orphanedTransactions.map((t) => - buildTransactionOutput(t), - ); + if (!baseTx) return null; return { ...baseTx, - orphanedTransactions: orphanedTxs, + orphanedTransactions: [], }; }; diff --git a/indexer/src/kadena-server/resolvers/query/transactions-query-resolver.ts b/indexer/src/kadena-server/resolvers/query/transactions-query-resolver.ts index 4ef4db9b..7c3efae4 100644 --- a/indexer/src/kadena-server/resolvers/query/transactions-query-resolver.ts +++ b/indexer/src/kadena-server/resolvers/query/transactions-query-resolver.ts @@ -19,6 +19,13 @@ export const transactionsQueryResolver: QueryResolvers["transac minHeight, minimumDepth, } = args; + + if (!accountName && !fungibleName && !blockHash && !requestKey) { + throw new Error( + "At least one of accountName, fungibleName, blockHash, or requestKey must be provided", + ); + } + const output = await context.transactionRepository.getTransactions({ blockHash, accountName, diff --git a/indexer/src/kadena-server/resolvers/subscription/new-blocks-from-depth-subscription-resolver.ts b/indexer/src/kadena-server/resolvers/subscription/new-blocks-from-depth-subscription-resolver.ts index 071e0018..feaf438e 100644 --- a/indexer/src/kadena-server/resolvers/subscription/new-blocks-from-depth-subscription-resolver.ts +++ b/indexer/src/kadena-server/resolvers/subscription/new-blocks-from-depth-subscription-resolver.ts @@ -39,7 +39,8 @@ export const newBlocksFromDepthSubscriptionResolver: SubscriptionResolvers= args.minimumDepth + (!args.chainIds || args.chainIds.includes(chainId)) && + height >= args.minimumDepth ); }, ), diff --git a/indexer/src/kadena-server/resolvers/subscription/new-blocks-subscription-resolver.ts b/indexer/src/kadena-server/resolvers/subscription/new-blocks-subscription-resolver.ts index 259d5d78..05245b4d 100644 --- a/indexer/src/kadena-server/resolvers/subscription/new-blocks-subscription-resolver.ts +++ b/indexer/src/kadena-server/resolvers/subscription/new-blocks-subscription-resolver.ts @@ -1,27 +1,77 @@ import { withFilter } from "graphql-subscriptions"; import { ResolverContext } from "../../config/apollo-server-config"; -import { - SubscriptionNewBlocksArgs, - SubscriptionResolvers, -} from "../../config/graphql-types"; -import { blockQueryResolver } from "../query/block-query-resolver"; -import { NEW_BLOCKS_EVENT } from "./consts"; +import { SubscriptionResolvers } from "../../config/graphql-types"; + +import BlockModel from "../../../models/block"; // Sequelize model for blocks +import { Op } from "sequelize"; +import { blockValidator } from "../../repository/infra/schema-validator/block-schema-validator"; +import { BlockOutput } from "../../repository/application/block-repository"; + +async function* iteratorFn( + chainIds: string[], + context: ResolverContext, +): AsyncGenerator { + const startingTimestamp = new Date().getTime() / 1000; + + let lastBlockId: number | undefined; + + while (true) { + const newBlocks = await getNewBlocks( + chainIds, + startingTimestamp, + lastBlockId, + ); + + if (newBlocks.length > 0) { + lastBlockId = newBlocks[0].id; // Update the last block ID + yield newBlocks.map((block) => blockValidator.mapFromSequelize(block)); + } + + await new Promise((resolve) => setTimeout(resolve, 1000)); + } +} + +async function getNewBlocks( + chainIds: string[], + date: number, + lastBlockId?: number, +) { + return BlockModel.findAll({ + where: { + ...(lastBlockId && { id: { [Op.gt]: lastBlockId } }), + creationTime: { [Op.gt]: date }, + ...(chainIds.length && { chainId: { [Op.in]: chainIds } }), + }, + limit: 100, + order: [["id", "DESC"]], + }); +} export const newBlocksSubscriptionResolver: SubscriptionResolvers["newBlocks"] = { - resolve: async (payload: any, _args: any, context: ResolverContext) => { - const res = await (blockQueryResolver as any)({}, payload, context); - return [res]; - }, - subscribe: (_parent, args: SubscriptionNewBlocksArgs, context) => { + subscribe: async (_parent, args, context) => { + const chainIds = args.chainIds ?? []; + + const iterator = withFilter( + () => iteratorFn(chainIds, context), + (payload, variables) => { + let subscribedChainIds; + if (variables) { + subscribedChainIds = variables.chainIds; + } else { + subscribedChainIds = []; + } + console.log("subscribedChainIds", subscribedChainIds); + return ( + !subscribedChainIds.length || + subscribedChainIds.includes(payload.chainId) + ); + }, + )(); + return { - [Symbol.asyncIterator]: withFilter( - () => context.pubSub.asyncIterator(NEW_BLOCKS_EVENT), - (payload) => { - const { chainId } = payload; - return args.chainIds.includes(chainId); - }, - ), + [Symbol.asyncIterator]: () => iterator, }; }, + resolve: (payload: any) => payload, }; diff --git a/indexer/src/kadena-server/server.ts b/indexer/src/kadena-server/server.ts index 013ebd48..94f09f05 100644 --- a/indexer/src/kadena-server/server.ts +++ b/indexer/src/kadena-server/server.ts @@ -1,7 +1,7 @@ import { ApolloServer, ApolloServerPlugin } from "@apollo/server"; import { expressMiddleware } from "@apollo/server/express4"; import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer"; -import express from "express"; +import express, { NextFunction, Request, Response } from "express"; import http from "http"; import cors from "cors"; import { resolvers } from "./resolvers"; @@ -15,7 +15,7 @@ import { import { WebSocketServer } from "ws"; import { useServer } from "graphql-ws/lib/use/ws"; import { makeExecutableSchema } from "@graphql-tools/schema"; -import { GraphQLError } from "graphql"; +import { ArgumentNode, ASTNode, GraphQLError, Kind, ValueNode } from "graphql"; import { EVENTS_EVENT, NEW_BLOCKS_EVENT, @@ -25,6 +25,7 @@ import { import { dispatchInfoSchema } from "../jobs/publisher-job"; import initCache from "../cache/init"; import { getRequiredEnvString } from "../utils/helpers"; +import ipRangeCheck from "ip-range-check"; const typeDefs = readFileSync( join(__dirname, "./config/schema.graphql"), @@ -35,19 +36,86 @@ const KADENA_GRAPHQL_API_PORT = getRequiredEnvString("KADENA_GRAPHQL_API_PORT"); const validatePaginationParamsPlugin: ApolloServerPlugin = { requestDidStart: async () => ({ - didResolveOperation: async ({ request }) => { - const variables = request.variables || {}; // Provide a default empty object if undefined - const { after, before } = variables; + didResolveOperation: async ({ request, document }) => { + const variables = { ...request.variables }; // External variables + const inlineArguments: Record = {}; + + // Helper function to extract inline arguments + const extractArguments = (node: ASTNode) => { + if (node.kind === Kind.FIELD && node.arguments) { + node.arguments.forEach((arg: ArgumentNode) => { + if (arg.value.kind === Kind.STRING) { + inlineArguments[arg.name.value] = arg.value.value; + } else if (arg.value.kind === Kind.INT) { + inlineArguments[arg.name.value] = parseInt(arg.value.value, 10); + } else if (arg.value.kind === Kind.FLOAT) { + inlineArguments[arg.name.value] = parseFloat(arg.value.value); + } else if (arg.value.kind === Kind.BOOLEAN) { + inlineArguments[arg.name.value] = arg.value.value === true; + } + }); + } + if (node.kind === Kind.SELECTION_SET) { + node.selections.forEach((selection) => extractArguments(selection)); + } + }; + + // Traverse the query AST to extract inline arguments + if (document) { + document.definitions.forEach((definition) => { + if ( + definition.kind === Kind.OPERATION_DEFINITION && + definition.selectionSet + ) { + extractArguments(definition.selectionSet); + } + }); + } + + // Combine variables and inline arguments + const combinedVariables = { ...inlineArguments, ...variables }; + const { after, before, first, last } = combinedVariables; - // Check if both after and before are passed + // Validation logic if (after && before) { throw new GraphQLError( 'You cannot use both "after" and "before" at the same time. Please use only one or none.', ); } + + if (first && last) { + throw new GraphQLError( + 'You cannot use both "first" and "last" at the same time. Please use only one or none.', + ); + } + + if (before && first) { + throw new GraphQLError( + 'You cannot use both "before" and "first" at the same time. Use before with last or after with first instead.', + ); + } + + if (after && last) { + throw new GraphQLError( + 'You cannot use both "after" and "last" at the same time. Use before with last or after with first instead.', + ); + } }, }), }; +const allowedCIDRs = ["10.0.2.0/24", "10.0.3.0/24"]; + +const ipFilterMiddleware = ( + req: Request, + res: Response, + next: NextFunction, +) => { + if (req.ip && ipRangeCheck(req.ip, allowedCIDRs)) { + next(); // Allow access + } else { + res.status(403).json({ message: "Access denied: IP not allowed" }); + } +}; export async function useKadenaGraphqlServer() { const app = express(); @@ -97,7 +165,7 @@ export async function useKadenaGraphqlServer() { }), ); - app.post("/new-block", async (req, res) => { + app.post("/new-block", ipFilterMiddleware, async (req, res) => { const payload = await dispatchInfoSchema.safeParseAsync(req.body); if (!payload.success) { return res.status(400).json({ message: "Invalid input" }); diff --git a/indexer/src/kadena-server/utils/coin-circulation.ts b/indexer/src/kadena-server/utils/coin-circulation.ts new file mode 100644 index 00000000..c288a17c --- /dev/null +++ b/indexer/src/kadena-server/utils/coin-circulation.ts @@ -0,0 +1,117 @@ +import Papa from "papaparse"; +import fs from "fs"; + +type RewardRow = [number, number]; + +function calculateReward(csvContent: string, cutHeight: number): number { + const parsed = Papa.parse(csvContent, { + delimiter: ",", + skipEmptyLines: true, + transformHeader: undefined, + transform: (value, fieldIndex) => + fieldIndex === 0 ? parseInt(value, 10) : parseFloat(value), + }); + + const averageHeight = cutHeight / 20; // number of chains + + let totalReward = 0; + let previousRow: RewardRow | null = null; + + for (const row of parsed.data) { + const height = row[0]; + const reward = row[1]; + + if (averageHeight < height) { + if (previousRow) { + const remainingHeight = height - averageHeight; + const proportionedReward = remainingHeight * reward; + totalReward += proportionedReward; + } + break; + } + + totalReward += reward; + previousRow = row; + } + + return totalReward; +} + +type CsvRow = [string, string, string, number, number]; + +function calculateTokenPayments( + csvContent: string, + targetTimestamp: number, +): number { + const parsed = Papa.parse(csvContent, { + delimiter: ",", + skipEmptyLines: true, + transform: (value, fieldIndex) => { + if (fieldIndex === 3 || fieldIndex === 4) { + return parseFloat(value); + } + return value; + }, + }); + + let totalSum = 0; + let previousRow: CsvRow | null = null; + + for (const row of parsed.data) { + const creationTimeISO = row[1]; + const amount = row[3]; + + const creationTimestamp = new Date(creationTimeISO).getTime(); + + if (targetTimestamp < creationTimestamp) { + if (previousRow) { + const remainingTime = creationTimestamp - targetTimestamp; + const proportionedPayment = remainingTime * amount; + totalSum += proportionedPayment; + } + break; + } + + totalSum += amount; + previousRow = row; + } + + return totalSum; +} + +async function getCsvContent(filePath: string): Promise { + return new Promise((resolve, reject) => { + fs.readFile(filePath, "utf-8", (err, data) => { + if (err) { + reject(err); + } else { + resolve(data); + } + }); + }); +} + +async function getMinerRewards(cutHeight: number) { + const csvContent = await getCsvContent( + `${__dirname}/../../../../csvs/miner_rewards.csv`, + ); + const reward = calculateReward(csvContent, cutHeight); + return reward; +} + +async function getTokenPayments(latestCreationTime: number) { + const csvContent = await getCsvContent( + `${__dirname}/../../../../csvs/token_payments.csv`, + ); + const tokenPayments = calculateTokenPayments(csvContent, latestCreationTime); + return tokenPayments; +} + +export async function getCirculationNumber( + cutHeight: number, + latestCreationTime: number, +) { + const minerRewards = await getMinerRewards(cutHeight); + const tokenPayments = await getTokenPayments(latestCreationTime); + return minerRewards + tokenPayments; +} diff --git a/indexer/src/kadena-server/utils/difficulty.ts b/indexer/src/kadena-server/utils/difficulty.ts index f44ab56e..df2ac85b 100644 --- a/indexer/src/kadena-server/utils/difficulty.ts +++ b/indexer/src/kadena-server/utils/difficulty.ts @@ -54,9 +54,6 @@ export function calculateTotalDifficulty( } return totalDifficulty; } - - // If we don't have enough blocks, use the previous block's difficulty. - return BigInt(-1); } } diff --git a/indexer/src/models/block.ts b/indexer/src/models/block.ts index 70eca270..ae649456 100644 --- a/indexer/src/models/block.ts +++ b/indexer/src/models/block.ts @@ -212,6 +212,10 @@ Block.init( name: "blocks_canonical_idx", fields: ["canonical"], }, + { + name: "blocks_height_id_idx", + fields: ["height", "id"], + }, // Search indexes { name: "blocks_trgm_parent_idx", diff --git a/indexer/src/models/event.ts b/indexer/src/models/event.ts index 13131c2c..e0fe3847 100644 --- a/indexer/src/models/event.ts +++ b/indexer/src/models/event.ts @@ -105,6 +105,10 @@ Event.init( name: "events_transactionid_idx", fields: ["transactionId"], }, + { + name: "events_module_name_idx", + fields: ["module", "name"], + }, ], }, ); diff --git a/indexer/src/models/transaction.ts b/indexer/src/models/transaction.ts index 9eb2931a..84f5e64b 100644 --- a/indexer/src/models/transaction.ts +++ b/indexer/src/models/transaction.ts @@ -260,6 +260,10 @@ Transaction.init( name: "transactions_canonical_idx", fields: ["canonical"], }, + { + name: "transactions_sender_id_idx", + fields: ["sender", "id"], + }, // Search indexes { name: "transactions_trgm_requestkey_idx", diff --git a/indexer/src/models/transfer.ts b/indexer/src/models/transfer.ts index 7d33d634..bfd3e853 100644 --- a/indexer/src/models/transfer.ts +++ b/indexer/src/models/transfer.ts @@ -192,6 +192,14 @@ Transfer.init( name: "transfers_chainid_to_acct_modulename_idx", fields: ["chainId", "to_acct", "modulename"], }, + { + name: "from_acct_idx", + fields: ["from_acct"], + }, + { + name: "to_acct_idx", + fields: ["to_acct"], + }, ], }, ); diff --git a/indexer/src/services/sync/guards.ts b/indexer/src/services/sync/guards.ts index 890e753b..5163c0a9 100644 --- a/indexer/src/services/sync/guards.ts +++ b/indexer/src/services/sync/guards.ts @@ -1,5 +1,5 @@ import pLimit from "p-limit"; -import { rootPgPool, sequelize } from "../../config/database"; +import { closeDatabase, rootPgPool, sequelize } from "../../config/database"; import { getGuardsFromBalances } from "./payload"; import Guard from "../../models/guard"; @@ -7,12 +7,11 @@ const CONCURRENCY_LIMIT = 4; // Number of concurrent fetches allowed const limitFetch = pLimit(CONCURRENCY_LIMIT); export async function startGuardsBackfill() { - try { - await sequelize.authenticate(); - console.log("Connected to the database."); + await sequelize.authenticate(); + console.log("Connected to the database."); - await rootPgPool.query( - ` + await rootPgPool.query( + ` BEGIN; SET enable_seqscan = OFF; WITH combined AS ( @@ -22,75 +21,71 @@ export async function startGuardsBackfill() { SELECT "chainId", "to_acct" AS "account", "modulename" AS "module" FROM "Transfers" ) - INSERT INTO "Balances" ("chainId", "account", "module", "createdAt", "updatedAt") - SELECT "chainId", "account", "module", NOW() AS "createdAt", NOW() AS "updatedAt" + INSERT INTO "Balances" ("chainId", "account", "module", "createdAt", "updatedAt", "tokenId") + SELECT "chainId", "account", "module", NOW() AS "createdAt", NOW() AS "updatedAt", '' AS "tokenId" FROM combined - GROUP BY "chainId", "account", "module"; + GROUP BY "chainId", "account", "module" + ON CONFLICT ("chainId", "account", "module", "tokenId") DO NOTHING; + DELETE FROM "Guards"; + ALTER SEQUENCE "Guards_id_seq" RESTART WITH 1; COMMIT; `, - ); - - console.log("Balances backfilled successfully."); - console.log("Starting guards backfill ..."); + ); - const limit = 500; // Number of rows to process in one batch - let offset = 0; + console.log("Balances backfilled successfully."); + console.log("Starting guards backfill ..."); - while (true) { - const tx = await sequelize.transaction(); - try { - console.log(`Fetching rows from offset: ${offset}, limit: ${limit}`); - const res = await rootPgPool.query( - `SELECT b.id, b.account, b."chainId", b.module FROM "Balances" b ORDER BY b.id LIMIT $1 OFFSET $2`, - [limit, offset], - ); + const limit = 10000; // Number of rows to process in one batch + let offset = 0; - const rows = res.rows; - if (rows.length === 0) { - console.log("No more rows to process."); - break; - } + while (true) { + console.log(`Fetching rows from offset: ${offset}, limit: ${limit}`); + const res = await rootPgPool.query( + `SELECT b.id, b.account, b."chainId", b.module FROM "Balances" b ORDER BY b.id LIMIT $1 OFFSET $2`, + [limit, offset], + ); - // Use p-limit to ensure controlled concurrency for fetch requests - const fetchPromises = rows.map((row) => - limitFetch(() => - getGuardsFromBalances([ - { - id: row.id, - account: row.account, - chainId: row.chainId, - module: row.module, - }, - ]), - ), - ); - const guards = (await Promise.all(fetchPromises)).flat(); + const rows = res.rows; + if (rows.length === 0) { + console.log("No more rows to process."); + break; + } - await Guard.bulkCreate(guards, { - transaction: tx, - }); + // Use p-limit to ensure controlled concurrency for fetch requests + const fetchPromises = rows.map((row) => + limitFetch(() => + getGuardsFromBalances([ + { + id: row.id, + account: row.account, + chainId: row.chainId, + module: row.module, + }, + ]), + ), + ); + const guards = (await Promise.all(fetchPromises)).flat(); + const tx = await sequelize.transaction(); + try { + await Guard.bulkCreate(guards, { + transaction: tx, + }); - await tx.commit(); - console.log(`Batch at offset ${offset} processed successfully.`); - offset += limit; - } catch (batchError) { - console.error( - `Error processing batch at offset ${offset}:`, - batchError, - ); - try { - await tx.rollback(); - console.log(`Transaction for batch at offset ${offset} rolled back.`); - } catch (rollbackError) { - console.error("Error during rollback:", rollbackError); - } - break; + await tx.commit(); + console.log(`Batch at offset ${offset} processed successfully.`); + offset += limit; + } catch (batchError) { + console.error(`Error processing batch at offset ${offset}:`, batchError); + try { + await tx.rollback(); + console.log(`Transaction for batch at offset ${offset} rolled back.`); + } catch (rollbackError) { + console.error("Error during rollback:", rollbackError); } + break; } - } catch (error) { - console.error("Error during backfill:", error); - } finally { - await sequelize.close(); - console.log("Database connection closed."); } + + await closeDatabase(); + process.exit(0); } diff --git a/indexer/src/services/sync/payload.ts b/indexer/src/services/sync/payload.ts index 2be52aee..8daca587 100644 --- a/indexer/src/services/sync/payload.ts +++ b/indexer/src/services/sync/payload.ts @@ -54,7 +54,7 @@ export async function processTransaction( throw error; } - let nonce = (cmdData.Nonce || "").replace(/\\"/g, ""); + let nonce = (cmdData.nonce || "").replace(/\\"/g, ""); nonce = nonce.replace(/"/g, ""); const eventsData = receiptInfo.events || []; const transactionAttributes = { diff --git a/indexer/src/services/sync/streaming.ts b/indexer/src/services/sync/streaming.ts index 8e12981e..47d1c8dc 100644 --- a/indexer/src/services/sync/streaming.ts +++ b/indexer/src/services/sync/streaming.ts @@ -1,7 +1,7 @@ import { processPayloadKey } from "./payload"; -import { delay, getDecoded, getRequiredEnvString } from "../../utils/helpers"; +import { getDecoded, getRequiredEnvString } from "../../utils/helpers"; import EventSource from "eventsource"; -import { dispatch, DispatchInfo } from "../../jobs/publisher-job"; +import { DispatchInfo } from "../../jobs/publisher-job"; import { uint64ToInt64 } from "../../utils/int-uint-64"; import Block, { BlockAttributes } from "../../models/block"; import { sequelize } from "../../config/database"; @@ -24,7 +24,7 @@ export async function startStreaming() { console.error("Connection error:", error); }; - eventSource.addEventListener("BlockHeader", (event: any) => { + eventSource.addEventListener("BlockHeader", async (event: any) => { try { const block = JSON.parse(event.data); const payload = processPayload(block.payloadWithOutputs); @@ -32,60 +32,26 @@ export async function startStreaming() { return; } blocksAlreadyReceived.add(block.header.hash); - blocksQueue.push({ header: block.header, payload }); - } catch (error) { - console.log(error); - } - }); - - setInterval(async () => { - const blocksToProcess = []; - while (blocksQueue.length > 0) { - const b = blocksQueue.shift(); - blocksToProcess.push(b); - } - - console.log("Processing blocks:", blocksToProcess.length); - const promises = blocksToProcess.map(async (block: any) => { - const blockData = await saveBlock(block); + const blockData = await saveBlock({ header: block.header, payload }); if (blockData === null) { await StreamingError.create({ hash: block.header.hash, chainId: block.header.chainId, }); + return; } - return blockData; - }); - - const processed = (await Promise.all(promises)).filter( - (r) => r !== null || r !== undefined, - ) as DispatchInfo[]; - - const dispatches = processed.map(async (r) => { - try { - await delay(500); - await dispatch(r); - } catch (err) { - console.error("Error dispatching block:", err); - } - }); - - await Promise.all(dispatches); - console.log( - "Processed:", - processed.length, - "|", - "Dispatched:", - dispatches.length, - ); - }, 1000 * 10); + blocksQueue.push(blockData); + } catch (error) { + console.log(error); + } + }); setInterval( () => { console.log("Clearing blocks already received."); blocksAlreadyReceived.clear(); }, - 1000 * 60 * 5, + 1000 * 60 * 10, ); } diff --git a/indexer/tests/unit/pagination.test.ts b/indexer/tests/unit/pagination.test.ts new file mode 100644 index 00000000..bbb4c311 --- /dev/null +++ b/indexer/tests/unit/pagination.test.ts @@ -0,0 +1,311 @@ +import { + encodeCursor, + getPageInfo, +} from "../../src/kadena-server/repository/pagination"; + +const PAGE_SIZE = 5 + 1; + +describe("Pagination - DESC", () => { + it("ROWS_LENGTH = PAGE_SIZE + 1", async () => { + const limit = PAGE_SIZE; + const order = "DESC"; + + const edges = ["6", "5", "4", "3", "2", "1"].map((cursor) => ({ + cursor, + node: { id: "_" }, + })); + const expectEdges = edges + .map((e) => ({ cursor: encodeCursor(e.cursor), node: e.node })) + .slice(0, 5); + + const output = getPageInfo({ + edges, + limit, + order, + }); + + expect(output).toEqual({ + pageInfo: { + hasNextPage: true, + hasPreviousPage: false, + startCursor: "Ng==", + endCursor: "Mg==", + }, + edges: expectEdges, + }); + }); + + it("ROWS_LENGTH = PAGE_SIZE", async () => { + const limit = PAGE_SIZE; + const order = "DESC"; + + const edges = ["5", "4", "3", "2", "1"].map((cursor) => ({ + cursor, + node: { id: "_" }, + })); + const expectEdges = edges.map((e) => ({ + cursor: encodeCursor(e.cursor), + node: e.node, + })); + + const output = getPageInfo({ + edges, + limit, + order, + }); + + expect(output).toEqual({ + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + startCursor: "NQ==", + endCursor: "MQ==", + }, + edges: expectEdges, + }); + }); + + it("ROWS_LENGTH < PAGE_SIZE", async () => { + const limit = PAGE_SIZE; + const order = "DESC"; + + const edges = ["4", "3", "2", "1"].map((cursor) => ({ + cursor, + node: { id: "_" }, + })); + const expectEdges = edges.map((e) => ({ + cursor: encodeCursor(e.cursor), + node: e.node, + })); + + const output = getPageInfo({ + edges, + limit, + order, + }); + + expect(output).toEqual({ + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + startCursor: "NA==", + endCursor: "MQ==", + }, + edges: expectEdges, + }); + }); + + it("hasPreviousPage = true", async () => { + const limit = PAGE_SIZE; + const order = "DESC"; + + const edges = ["5", "4", "3", "2", "1"].map((cursor) => ({ + cursor, + node: { id: "_" }, + })); + const expectEdges = edges.map((e) => ({ + cursor: encodeCursor(e.cursor), + node: e.node, + })); + + const output = getPageInfo({ + edges, + limit, + order, + after: "6", + }); + + expect(output).toEqual({ + pageInfo: { + hasNextPage: false, + hasPreviousPage: true, + startCursor: "NQ==", + endCursor: "MQ==", + }, + edges: expectEdges, + }); + }); + + it("hasPreviousPage = false", async () => { + const limit = PAGE_SIZE; + const order = "DESC"; + + const edges = ["5", "4", "3", "2", "1"].map((cursor) => ({ + cursor, + node: { id: "_" }, + })); + const expectEdges = edges.map((e) => ({ + cursor: encodeCursor(e.cursor), + node: e.node, + })); + + const output = getPageInfo({ + edges, + limit, + order, + }); + + expect(output).toEqual({ + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + startCursor: "NQ==", + endCursor: "MQ==", + }, + edges: expectEdges, + }); + }); +}); + +describe("Pagination - ASC", () => { + it("ROWS_LENGTH = PAGE_SIZE + 1", async () => { + const limit = PAGE_SIZE; + const order = "ASC"; + + const edges = ["1", "2", "3", "4", "5", "6"].map((cursor) => ({ + cursor, + node: { id: "_" }, + })); + const edgesExpected = ["5", "4", "3", "2", "1"].map((cursor) => ({ + cursor: encodeCursor(cursor), + node: { id: "_" }, + })); + + const output = getPageInfo({ + edges, + limit, + order, + }); + + expect(output).toEqual({ + pageInfo: { + hasNextPage: false, + hasPreviousPage: true, + startCursor: "NQ==", + endCursor: "MQ==", + }, + edges: edgesExpected, + }); + }); + + it("ROWS_LENGTH = PAGE_SIZE", async () => { + const limit = PAGE_SIZE; + const order = "ASC"; + + const edges = ["1", "2", "3", "4", "5"].map((cursor) => ({ + cursor, + node: { id: "_" }, + })); + const edgesExpected = ["5", "4", "3", "2", "1"].map((cursor) => ({ + cursor: encodeCursor(cursor), + node: { id: "_" }, + })); + + const output = getPageInfo({ + edges, + limit, + order, + }); + + expect(output).toEqual({ + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + startCursor: "NQ==", + endCursor: "MQ==", + }, + edges: edgesExpected, + }); + }); + + it("ROWS_LENGTH < PAGE_SIZE", async () => { + const limit = PAGE_SIZE; + const order = "ASC"; + + const edges = ["1", "2", "3", "4"].map((cursor) => ({ + cursor, + node: { id: "_" }, + })); + const edgesExpected = ["4", "3", "2", "1"].map((cursor) => ({ + cursor: encodeCursor(cursor), + node: { id: "_" }, + })); + + const output = getPageInfo({ + edges, + limit, + order, + }); + + expect(output).toEqual({ + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + startCursor: "NA==", + endCursor: "MQ==", + }, + edges: edgesExpected, + }); + }); + + it("hasPreviousPage = true", async () => { + const limit = PAGE_SIZE; + const order = "ASC"; + + const edges = ["1", "2", "3", "4", "5"].map((cursor) => ({ + cursor, + node: { id: "_" }, + })); + const edgesExpected = ["5", "4", "3", "2", "1"].map((cursor) => ({ + cursor: encodeCursor(cursor), + node: { id: "_" }, + })); + + const output = getPageInfo({ + edges, + limit, + order, + before: "6", + }); + + expect(output).toEqual({ + pageInfo: { + hasNextPage: true, + hasPreviousPage: false, + startCursor: "NQ==", + endCursor: "MQ==", + }, + edges: edgesExpected, + }); + }); + + it("hasPreviousPage = false", async () => { + const limit = PAGE_SIZE; + const order = "ASC"; + + const edges = ["1", "2", "3", "4", "5"].map((cursor) => ({ + cursor, + node: { id: "_" }, + })); + const edgesExpected = ["5", "4", "3", "2", "1"].map((cursor) => ({ + cursor: encodeCursor(cursor), + node: { id: "_" }, + })); + + const output = getPageInfo({ + edges, + limit, + order, + }); + + expect(output).toEqual({ + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + startCursor: "NQ==", + endCursor: "MQ==", + }, + edges: edgesExpected, + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 7cfb5d26..6a563b5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4610,6 +4610,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== +"@types/papaparse@^5.3.15": + version "5.3.15" + resolved "https://registry.yarnpkg.com/@types/papaparse/-/papaparse-5.3.15.tgz#7cafa16757a1d121422deefbb10b6310b224ecc4" + integrity sha512-JHe6vF6x/8Z85nCX4yFdDslN11d+1pr12E526X8WAfhadOeaOTx5AuIkvDKIBopfvlzpzkdMx4YyvSKCM9oqtw== + dependencies: + "@types/node" "*" + "@types/pg@>=6 <9": version "8.11.6" resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.11.6.tgz#a2d0fb0a14b53951a17df5197401569fb9c0c54b" @@ -5859,7 +5866,7 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -bignumber.js@^9.0.0, bignumber.js@^9.1.2: +bignumber.js@^9.1.2: version "9.1.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== @@ -8648,7 +8655,14 @@ ip-address@^9.0.5: jsbn "1.1.0" sprintf-js "^1.1.3" -ipaddr.js@1.9.1: +ip-range-check@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ip-range-check/-/ip-range-check-0.2.0.tgz#e67f126c8fb36c8f11d4c07d7924b7e364365157" + integrity sha512-oaM3l/3gHbLlt/tCWLvt0mj1qUaI+STuRFnUvARGCujK9vvU61+2JsDpmkMzR4VsJhuFXWWgeKKVnwwoFfzCqw== + dependencies: + ipaddr.js "^1.0.1" + +ipaddr.js@1.9.1, ipaddr.js@^1.0.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== @@ -9364,13 +9378,6 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -json-bigint@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" - integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== - dependencies: - bignumber.js "^9.0.0" - json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -10877,6 +10884,11 @@ pacote@^18.0.6: ssri "^10.0.0" tar "^6.1.11" +papaparse@^5.5.2: + version "5.5.2" + resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.5.2.tgz#fb67cc5a03ba8930cb435dc4641a25d6804bd4d7" + integrity sha512-PZXg8UuAc4PcVwLosEEDYjPyfWnTEhOrUfdv+3Bx+NuAb+5NhDmXzg5fHWmdCh1mP5p7JAZfFr3IMQfcntNAdA== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"