package zcashtokenservice import ( "cwtch.im/tapir/primitives/core" "cwtch.im/tapir/primitives/privacypass" "encoding/base64" "encoding/json" "errors" "fmt" "git.openprivacy.ca/openprivacy/libricochet-go/log" "github.com/gtank/ristretto255" "golang.org/x/crypto/sha3" "strings" ) type Context struct { Y *ristretto255.Element tokens []*privacypass.Token blindedTokens []privacypass.BlindedToken transcript *core.Transcript ct *privacypass.Token } type ZcashTokenRequest struct { BlindedTokens []privacypass.BlindedToken `json:"bts"` ReturnAddress string `json:"ra"` ConstraintToken []byte `json:"ct"` } // RequestTokens generates a zcash uri func RequestTokenURI(tokenServiceZcashAddress string, tokenServicePublicKey *ristretto255.Element, zcashReturnAddress string, constraintToken *privacypass.Token) (Context, string) { tokens, blinded := privacypass.GenerateBlindedTokenBatch(4) transcript := core.NewTranscript("zcash-token-service") transcript.AddToTranscript(zcashReturnAddress, []byte(zcashReturnAddress)) transcript.AddToTranscript("zcash-token-server", []byte(tokenServiceZcashAddress)) request := ZcashTokenRequest{BlindedTokens: blinded, ReturnAddress: zcashReturnAddress} if constraintToken != nil { transcript.AddToTranscript("constraint-token-t", constraintToken.GetT()) request.ConstraintToken = constraintToken.GetT() } data, _ := json.Marshal(request) // NOTE: We are currently hardcoding this amount! return Context{tokens: tokens, blindedTokens: blinded, Y: tokenServicePublicKey, transcript: transcript, ct: constraintToken}, fmt.Sprintf("zcash:%v?amt=%f&memo=%v", tokenServiceZcashAddress, 0.1, base64.StdEncoding.EncodeToString(data)) } type ZcashTokenResponse struct { privacypass.SignedBatchWithProof `json:"sb"` Error error `json:",omitempty"` } func ProcessRequest(request string, tokenServiceZcashAddress string, server *privacypass.TokenServer) (string, string) { log.Infof("Request: [%x]", request) data, err := base64.StdEncoding.DecodeString(request) log.Infof("Err: %s", data) if err == nil { zcashTokenRequest := new(ZcashTokenRequest) err = json.Unmarshal(data, zcashTokenRequest) if err == nil { transcript := core.NewTranscript("zcash-token-service") transcript.AddToTranscript(zcashTokenRequest.ReturnAddress, []byte(zcashTokenRequest.ReturnAddress)) transcript.AddToTranscript("zcash-token-server", []byte(tokenServiceZcashAddress)) if len(zcashTokenRequest.ConstraintToken) == 0 { signedBatchWithProof := server.SignBlindedTokenBatch(zcashTokenRequest.BlindedTokens, transcript) data, _ := json.Marshal(ZcashTokenResponse{signedBatchWithProof, nil}) return base64.StdEncoding.EncodeToString(data), zcashTokenRequest.ReturnAddress } else { transcript.AddToTranscript("constraint-token-t", zcashTokenRequest.ConstraintToken) signedBatchWithProof := server.SignBlindedTokenBatchWithConstraint(zcashTokenRequest.BlindedTokens, zcashTokenRequest.ConstraintToken, transcript) log.Debugf("Server Side Transcript: %s", transcript.OutputTranscriptToAudit()) data, _ := json.Marshal(ZcashTokenResponse{signedBatchWithProof, nil}) return base64.StdEncoding.EncodeToString(data), zcashTokenRequest.ReturnAddress } } } errorresponse, _ := json.Marshal(ZcashTokenResponse{privacypass.SignedBatchWithProof{}, err}) return base64.StdEncoding.EncodeToString(errorresponse), "" } func ProcessResponse(response string, ctx Context) ([]*privacypass.Token, error) { response = strings.ReplaceAll(response, "\n", "") response = strings.ReplaceAll(response, "\r", "") data, err := base64.StdEncoding.DecodeString(response) if err == nil { zcashTokenResponse := new(ZcashTokenResponse) err = json.Unmarshal(data, zcashTokenResponse) if err == nil { if ctx.ct == nil { if privacypass.UnblindSignedTokenBatch(ctx.tokens, ctx.blindedTokens, zcashTokenResponse.SignedTokens, ctx.Y, zcashTokenResponse.Proof, ctx.transcript) { return ctx.tokens, nil } } else { Ht := sha3.Sum512(ctx.ct.GetT()) T := new(ristretto255.Element).FromUniformBytes(Ht[:]) if privacypass.UnblindSignedTokenBatch(ctx.tokens, append(ctx.blindedTokens, privacypass.BlindedToken{T}), append(zcashTokenResponse.SignedTokens, privacypass.SignedToken{ctx.ct.W}), ctx.Y, zcashTokenResponse.Proof, ctx.transcript) { return ctx.tokens, nil } } err = errors.New("failed to unblind signed tokens") } } return nil, err }