Exemple #1
0
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
}
Exemple #2
0
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
	}
}
Exemple #3
0
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
	}
}
Exemple #4
0
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
}
Exemple #5
0
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
}
Exemple #6
0
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
	}
}
Exemple #7
0
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
}
Exemple #8
0
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
	}
}
Exemple #9
0
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
	}
}
Exemple #10
0
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
}
Exemple #11
0
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
}
Exemple #12
0
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
}