func (ps *PaymentService) balance(req *msg.OcReq) (*msg.OcResp, error) { p, err := peer.NewPeerFromReq(req, ps.BitcoindConf) if err != nil { return msg.NewRespError(msg.SERVER_ERROR), nil } balance, err := p.Balance(0, ps.BitcoindConf) if err != nil { return msg.NewRespError(msg.SERVER_ERROR), nil } maxBalanceConf, err := ps.Conf.PolicyForCmd(conf.MAX_BALANCE) if err != nil { // TODO(ortutay): handle more configuration around max balance panic(err) } maxBalance := maxBalanceConf.Args[0].(*msg.PaymentValue) btcAddr, err := p.PaymentAddr(ADDRS_PER_ID, ps.BitcoindConf) if err != nil { return msg.NewRespError(msg.SERVER_ERROR), nil } resp := BalanceResponse{ Balance: balance, MaxBalance: maxBalance, Addr: btcAddr, } body, err := json.Marshal(&resp) if err != nil { return msg.NewRespError(msg.SERVER_ERROR), nil } return msg.NewRespOk(body), nil }
func (ps *PaymentService) getPaymentAddr(req *msg.OcReq) (*msg.OcResp, error) { if len(req.Args) > 1 { return msg.NewRespError(msg.INVALID_ARGUMENTS), nil } reqCurrency := string(msg.BTC) if len(req.Args) == 1 { reqCurrency = strings.ToUpper(req.Args[0]) } p, err := peer.NewPeerFromReq(req, ps.BitcoindConf) if err != nil { return msg.NewRespError(msg.SERVER_ERROR), nil } switch reqCurrency { case string(msg.BTC): if ps.BitcoindConf == nil { return msg.NewRespError(msg.SERVER_ERROR), nil } // TODO(ortutay): smarter handling to map request ID to address btcAddr, err := p.PaymentAddr(ADDRS_PER_ID, ps.BitcoindConf) if err != nil { return msg.NewRespError(msg.SERVER_ERROR), nil } payAddr := msg.PaymentAddr{Currency: msg.BTC, Addr: btcAddr} return msg.NewRespOk([]byte(payAddr.String())), nil default: return msg.NewRespError(msg.CURRENCY_UNSUPPORTED), nil } }
func (sm *ServiceMux) Handle(req *msg.OcReq) (*msg.OcResp, error) { if service, ok := sm.Services[req.Service]; ok { return service.Handle(req) } else { return msg.NewRespError(msg.SERVICE_UNSUPPORTED), nil } }
func (ss *StoreService) get(req *msg.OcReq) (*msg.OcResp, error) { var containerID ContainerID var blobID BlobID if len(req.Args) == 1 { containerID = ocIDToContainerID(req.ID) blobID = BlobID(req.Args[0]) } else if len(req.Args) == 2 { if req.Args[0] == "." { containerID = ocIDToContainerID(req.ID) } else { containerID = ContainerID(req.Args[0]) } blobID = BlobID(req.Args[1]) } else { return msg.NewRespError(msg.INVALID_ARGUMENTS), nil } fmt.Printf("get %v %v\n", containerID, blobID) if containerID != ocIDToContainerID(req.ID) { resp := msg.NewRespErrorWithBody(msg.INVALID_ARGUMENTS, []byte("Cannot access that container")) return resp, nil } container := NewContainerFromDisk(req.ID) if !container.HasBlobID(blobID) { resp := msg.NewRespErrorWithBody(msg.INVALID_ARGUMENTS, []byte("Cannot access that blob")) return resp, nil } blob, err := NewBlobFromDisk(blobID) if err != nil { return msg.NewRespError(msg.SERVER_ERROR), nil } var buf bytes.Buffer for _, block := range blob.Blocks { _, err := buf.Write(block.Data) if err != nil { return msg.NewRespError(msg.SERVER_ERROR), nil } } return msg.NewRespOk(buf.Bytes()), nil }
func (cs CalcService) quote(req *msg.OcReq) (*msg.OcResp, error) { reqMethod := req.Args[0] var reqWork Work err := json.Unmarshal([]byte(req.Args[1]), &reqWork) if err != nil { return msg.NewRespError(msg.INVALID_ARGUMENTS), nil } if reqMethod != CALCULATE_METHOD { return msg.NewRespError(msg.INVALID_ARGUMENTS), nil } pv, err := cs.paymentForWork(&reqWork, reqMethod) if err != nil { log.Printf("server error: %v", err.Error()) return msg.NewRespError(msg.SERVER_ERROR), nil } resp := msg.NewRespOk([]byte(pv.String())) return resp, nil }
func (ps *PaymentService) Handle(req *msg.OcReq) (*msg.OcResp, error) { methods := make(map[string]func(*msg.OcReq) (*msg.OcResp, error)) methods[PAYMENT_ADDR_METHOD] = ps.getPaymentAddr methods[BALANCE_METHOD] = ps.balance if method, ok := methods[req.Method]; ok { return method(req) } else { return msg.NewRespError(msg.METHOD_UNSUPPORTED), nil } }
func (s *Server) checkBalance(p *peer.Peer) *msg.OcResp { balance, err := p.Balance(SERVER_PAYMENT_MIN_CONF, s.BtcConf) if err != nil { return msg.NewRespError(msg.SERVER_ERROR) } fmt.Printf("balance: %v\n", balance) if balance.Currency != msg.BTC { panic("TODO: support other currencies") } maxBalance, err := s.Conf.PolicyForCmd(conf.MAX_BALANCE) if err != nil { // TODO(ortutay): handle more configuration around max balance panic(err) } maxAllowed := maxBalance.Args[0].(*msg.PaymentValue).Amount fmt.Printf("max balance: %v\n", maxBalance.Args[0]) if balance.Amount > maxAllowed { addr, err := p.PaymentAddr(-1, s.BtcConf) if err != nil { return msg.NewRespError(msg.SERVER_ERROR) } // TODO(ortutay): a clever client will notice that they can pay off just a // small amount to stay just at the edge of the max balance. they do not // much by doing this, and may waste money on miner fees, but nevertheless, // we could have some smarter handling for that situation. pr := msg.PaymentRequest{ Amount: balance.Amount, Currency: balance.Currency, Addr: addr, } body, err := json.Marshal(&pr) if err != nil { return msg.NewRespError(msg.SERVER_ERROR) } return msg.NewRespErrorWithBody(msg.PLEASE_PAY, []byte(body)) } return nil }
func (cs CalcService) Handle(req *msg.OcReq) (*msg.OcResp, error) { println(fmt.Sprintf("calc got request: %v", req)) if req.Service != SERVICE_NAME { panic(fmt.Sprintf("unexpected service %s", req.Service)) } methods := make(map[string]func(*msg.OcReq) (*msg.OcResp, error)) methods[CALCULATE_METHOD] = cs.calculate methods[QUOTE_METHOD] = cs.quote if method, ok := methods[req.Method]; ok { return method(req) } else { return msg.NewRespError(msg.METHOD_UNSUPPORTED), nil } }
func (ss *StoreService) Handle(req *msg.OcReq) (*msg.OcResp, error) { println(fmt.Sprintf("store got request: %v", req)) if req.Service != SERVICE_NAME { panic(fmt.Sprintf("unexpected service %s", req.Service)) } methods := make(map[string]func(*msg.OcReq) (*msg.OcResp, error)) methods[ALLOC_METHOD] = ss.alloc methods[PUT_METHOD] = ss.put methods[GET_METHOD] = ss.get if method, ok := methods[req.Method]; ok { return method(req) } else { return msg.NewRespError(msg.METHOD_UNSUPPORTED), nil } }
func (s *Server) Serve(listener net.Listener) error { conn, err := listener.Accept() if err != nil { return err } go (func(conn net.Conn) { println("get req") req, err := msg.ReadOcReq(bufio.NewReader(conn)) defer conn.Close() defer fmt.Fprintf(conn, "\n") if err != nil { msg.NewRespError(msg.BAD_REQUEST).Write(conn) return } fmt.Printf("Got request: %v\n", req) // TODO(ortutay): implement additional request validation // - validate sigs // - check nonce // - check service available // - check method available p, err := peer.NewPeerFromReq(req, s.BtcConf) if err != nil { log.Printf("error generating peer: %v\n", err) if err == peer.INVALID_SIGNATURE { msg.NewRespError(msg.INVALID_SIGNATURE).Write(conn) } else if err == peer.COIN_REUSE { msg.NewRespError(msg.COIN_REUSE).Write(conn) } else { msg.NewRespError(msg.SERVER_ERROR).Write(conn) } return } // TODO(ortutay): more configuration options around allowed balance balanceDueResp := s.checkBalance(p) if balanceDueResp != nil && req.Service != "payment" { balanceDueResp.Write(conn) return } if ok, status := s.isAllowedByPolicy(p, req); !ok { if status == msg.OK { panic("expected error status") } fmt.Printf("not allowed: %v %v\n", ok, status) msg.NewRespError(status).Write(conn) return } fmt.Printf("passing off to handler...\n") resp, err := s.Handler.Handle(req) if err != nil || resp == nil { fmt.Printf("server error: %v\n", err) msg.NewRespError(msg.SERVER_ERROR).Write(conn) } else { fmt.Printf("sending response: %v\n", resp) resp.Write(conn) } return })(conn) return nil }
func (cs CalcService) calculate(req *msg.OcReq) (*msg.OcResp, error) { // TODO(ortutay): pull out payment verifcation logic work, err := Measure(req) if err != nil { log.Printf("server error: %v", err.Error()) return msg.NewRespError(msg.SERVER_ERROR), nil } pv, err := cs.paymentForWork(work, CALCULATE_METHOD) if err != nil { log.Printf("server error: %v", err.Error()) return msg.NewRespError(msg.SERVER_ERROR), nil } var submitTxn string if pv.Amount != 0 { fmt.Printf("want payment: %v, got payment: %v\n", pv, req.PaymentValue) if req.PaymentType == msg.NONE || req.PaymentValue == nil { return msg.NewRespError(msg.PAYMENT_REQUIRED), nil } if req.PaymentValue.Currency != pv.Currency { return msg.NewRespError(msg.CURRENCY_UNSUPPORTED), nil } if req.PaymentValue.Amount < pv.Amount { return msg.NewRespError(msg.TOO_LOW), nil } var repStatus rep.Status switch req.PaymentType { case msg.DEFER: // TODO(ortutay): check if we accept deferred payment for the request // TODO(ortutay): marking as success is pre-emptive; need to handle // failure case repStatus = rep.SUCCESS_UNPAID case msg.ATTACHED: if !btc.TxnIsValid(req.PaymentTxn, req.PaymentValue) { return msg.NewRespError(msg.INVALID_TXN), nil } submitTxn = req.PaymentTxn // TODO(ortutay): same note as above about pre-emptive success mark repStatus = rep.SUCCESS_PAID } rec := rep.Record{ Role: rep.SERVER, Service: SERVICE_NAME, Method: CALCULATE_METHOD, Timestamp: int(time.Now().Unix()), ID: req.ID, Status: repStatus, PaymentType: msg.DEFER, PaymentValue: pv, Perf: nil, } fmt.Printf("rep rec: %v\n", rec) id, err := rep.Put(&rec) if err != nil { return nil, fmt.Errorf("local database error: %v", err) } fmt.Printf("stored rep rec, %v %v\n", id, err) } var results []string for _, q := range req.Args { tokens := strings.Split(q, " ") var stack []float64 for _, token := range tokens { switch token { case "+": r := stack[len(stack)-1] + stack[len(stack)-2] stack = stack[0 : len(stack)-2] stack = append(stack, r) case "-": r := stack[len(stack)-2] - stack[len(stack)-1] stack = stack[0 : len(stack)-2] stack = append(stack, r) case "/": r := stack[len(stack)-2] / stack[len(stack)-1] stack = stack[0 : len(stack)-2] stack = append(stack, r) case "*": r := stack[len(stack)-1] * stack[len(stack)-2] stack = stack[0 : len(stack)-2] stack = append(stack, r) default: f, err := strconv.ParseFloat(token, 54) stack = append(stack, f) if err != nil { return nil, errors.New("didn't understand \"" + token + "\"") } } } if len(stack) != 1 { return nil, errors.New("invalid expression") } results = append(results, fmt.Sprintf("%v", stack[0])) } resp := msg.NewRespOk([]byte(strings.Join(results, " "))) if submitTxn != "" { _ = btc.SubmitTxn(submitTxn) } return resp, nil }
func (ss *StoreService) put(req *msg.OcReq) (*msg.OcResp, error) { var containerID ContainerID var blobID BlobID if len(req.Args) == 0 { containerID = ocIDToContainerID(req.ID) // blob will be read from request } else if len(req.Args) == 1 { if req.Args[0] == "." { containerID = ocIDToContainerID(req.ID) } else { containerID = ContainerID(req.Args[0]) } // blob will be read from request } else if len(req.Args) == 2 { if req.Args[0] == "." { containerID = ocIDToContainerID(req.ID) } else { containerID = ContainerID(req.Args[0]) } blobID = BlobID(req.Args[1]) } else { return msg.NewRespError(msg.INVALID_ARGUMENTS), nil } if containerID != ocIDToContainerID(req.ID) { resp := msg.NewRespErrorWithBody(msg.INVALID_ARGUMENTS, []byte("Cannot access that container")) return resp, nil } fmt.Printf("put request for: %v %v\n", containerID, blobID) // Store blob if it is new blob, err := NewBlobFromDisk(blobID) if blob == nil { if req.Body == nil || len(req.Body) == 0 { // TODO(ortutay): Neither "OK" nor "error" are appropriate status codes // in this case. It may be useful to have a third error class, but not // sure what to call it. return msg.NewRespOk([]byte("Please re-send with data.")), nil } if len(req.Body) > MAX_BLOB_BYTES { resp := msg.NewRespErrorWithBody(msg.CANNOT_COMPLETE_REQUEST, []byte(fmt.Sprintf("Cannot store over %v", util.ByteSize(MAX_BLOB_BYTES).String()))) return resp, nil } blob, err = NewBlobFromReader(bytes.NewReader(req.Body)) if err != nil { return msg.NewRespError(msg.SERVER_ERROR), nil } err := storeBlob(blob) if err != nil { return msg.NewRespError(msg.SERVER_ERROR), nil } } // Append blob-id to container-id container := NewContainerFromDisk(req.ID) for _, id := range container.BlobIDs { if id == blob.ID { return msg.NewRespOk([]byte("")), nil } } container.WriteNewBlobID(blob.ID) return msg.NewRespOk([]byte(blob.ID.String())), nil }