forked from TrueCloudLab/frostfs-api-go
initial
This commit is contained in:
commit
1cf33e5ffd
87 changed files with 29835 additions and 0 deletions
8
accounting/fixtures/cheque.sh
Executable file
8
accounting/fixtures/cheque.sh
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
CHEQUE=d6520dabb6cb9b981792608c73670eff14775e9a65bbc189271723ba2703c53263e8d6e522dc32203339dcd8eee9c6b7439a0000000053724e000000000000001e61000603012d47e76210aec73be39ab3d186e0a40fe8d86bfa3d4fabfda57ba13b88f96abe1de4c7ecd46cb32081c0ff199e0b32708d2ce709dd146ce096484073a9b15a259ca799f8d848eb5bea16f6d0842a0181ccd47384af2cdb0fd0af0819e8a08802f7528ce97c9a93558efe7d4f62577aabdf771c931f54a71be6ad21e7d9cc1777686ad19b5dc4b80d7b8decf90054c5aad66c0e6fe63d8473b751cd77c1bd0557516e0f3e7d0ccb485809023b0c08a89f33ae38b2f99ce3f1ebc7905dddf0ed0f023e00f03a16e8707ce045eb42ee80d392451541ee510dc18e1c8befbac54d7426087d37d32d836537d317deafbbd193002a36f80fbdfbf3a730cf011bc6c75c7e6d5724f3adee7015fcb3068d321e2ae555e79107be0c46070efdae2f724dbc9f0340750b92789821683283bcb98e32b7e032b94f267b6964613fc31a7ce5813fddeea47a1db525634237e924178b5c8ea745549ae60aa3570ce6cf52e370e6ab87652bdf8a179176f1acaf48896bef9ab300818a53f410d86241d506a550f4915403fef27f744e829131d0ec980829fafa51db1714c2761d9f78762c008c323e9d6612e4f9efdc609f191fd9ca5431dd9dc037130150107ab8769780d728e9ffdf314019b57c8d2b940b9ec078afa951ed8b06c1bf352edd2037e29b8f24cca3ec700368a6f5829fb2a34fa03d0308ae6b05f433f2904d9a852fed1f5d2eb598ca79475b74ef6394e712d275cd798062c6d8e41fad822ac5a4fcb167f0a2e196f61f9f65a0adef9650f49150e7eb7bb08dd1739fa6e86b341f1b2cf5657fcd200637e8
|
||||
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
|
||||
echo $CHEQUE | xxd -p -r > $DIR/cheque_data
|
||||
|
||||
exit 0
|
BIN
accounting/fixtures/cheque_data
Normal file
BIN
accounting/fixtures/cheque_data
Normal file
Binary file not shown.
49
accounting/service.go
Normal file
49
accounting/service.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package accounting
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neofs-proto/decimal"
|
||||
"github.com/nspcc-dev/neofs-proto/internal"
|
||||
"github.com/nspcc-dev/neofs-proto/refs"
|
||||
)
|
||||
|
||||
type (
|
||||
// OwnerID type alias.
|
||||
OwnerID = refs.OwnerID
|
||||
|
||||
// Decimal type alias.
|
||||
Decimal = decimal.Decimal
|
||||
|
||||
// Filter is used to filter accounts by criteria.
|
||||
Filter func(acc *Account) bool
|
||||
)
|
||||
|
||||
const (
|
||||
// ErrEmptyAddress is raised when passed Address is empty.
|
||||
ErrEmptyAddress = internal.Error("empty address")
|
||||
|
||||
// ErrEmptyLockTarget is raised when passed LockTarget is empty.
|
||||
ErrEmptyLockTarget = internal.Error("empty lock target")
|
||||
|
||||
// ErrEmptyContainerID is raised when passed CID is empty.
|
||||
ErrEmptyContainerID = internal.Error("empty container ID")
|
||||
|
||||
// ErrEmptyParentAddress is raised when passed ParentAddress is empty.
|
||||
ErrEmptyParentAddress = internal.Error("empty parent address")
|
||||
)
|
||||
|
||||
// SetTTL sets ttl to BalanceRequest to satisfy TTLRequest interface.
|
||||
func (m BalanceRequest) SetTTL(v uint32) { m.TTL = v }
|
||||
|
||||
// SumFunds goes through all accounts and sums up active funds.
|
||||
func SumFunds(accounts []*Account) (res *decimal.Decimal) {
|
||||
res = decimal.Zero.Copy()
|
||||
|
||||
for i := range accounts {
|
||||
if accounts[i] == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
res = res.Add(accounts[i].ActiveFunds)
|
||||
}
|
||||
return
|
||||
}
|
701
accounting/service.pb.go
Normal file
701
accounting/service.pb.go
Normal file
|
@ -0,0 +1,701 @@
|
|||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: accounting/service.proto
|
||||
|
||||
package accounting
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
_ "github.com/gogo/protobuf/gogoproto"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
decimal "github.com/nspcc-dev/neofs-proto/decimal"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type BalanceRequest struct {
|
||||
OwnerID OwnerID `protobuf:"bytes,1,opt,name=OwnerID,proto3,customtype=OwnerID" json:"OwnerID"`
|
||||
TTL uint32 `protobuf:"varint,2,opt,name=TTL,proto3" json:"TTL,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *BalanceRequest) Reset() { *m = BalanceRequest{} }
|
||||
func (m *BalanceRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*BalanceRequest) ProtoMessage() {}
|
||||
func (*BalanceRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_7f9514b8f1d4c7fe, []int{0}
|
||||
}
|
||||
func (m *BalanceRequest) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *BalanceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
func (m *BalanceRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_BalanceRequest.Merge(m, src)
|
||||
}
|
||||
func (m *BalanceRequest) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *BalanceRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_BalanceRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_BalanceRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *BalanceRequest) GetTTL() uint32 {
|
||||
if m != nil {
|
||||
return m.TTL
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type BalanceResponse struct {
|
||||
Balance *decimal.Decimal `protobuf:"bytes,1,opt,name=Balance,proto3" json:"Balance,omitempty"`
|
||||
LockAccounts []*Account `protobuf:"bytes,2,rep,name=LockAccounts,proto3" json:"LockAccounts,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *BalanceResponse) Reset() { *m = BalanceResponse{} }
|
||||
func (m *BalanceResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*BalanceResponse) ProtoMessage() {}
|
||||
func (*BalanceResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_7f9514b8f1d4c7fe, []int{1}
|
||||
}
|
||||
func (m *BalanceResponse) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *BalanceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
func (m *BalanceResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_BalanceResponse.Merge(m, src)
|
||||
}
|
||||
func (m *BalanceResponse) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *BalanceResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_BalanceResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_BalanceResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *BalanceResponse) GetBalance() *decimal.Decimal {
|
||||
if m != nil {
|
||||
return m.Balance
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *BalanceResponse) GetLockAccounts() []*Account {
|
||||
if m != nil {
|
||||
return m.LockAccounts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*BalanceRequest)(nil), "accounting.BalanceRequest")
|
||||
proto.RegisterType((*BalanceResponse)(nil), "accounting.BalanceResponse")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("accounting/service.proto", fileDescriptor_7f9514b8f1d4c7fe) }
|
||||
|
||||
var fileDescriptor_7f9514b8f1d4c7fe = []byte{
|
||||
// 311 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x48, 0x4c, 0x4e, 0xce,
|
||||
0x2f, 0xcd, 0x2b, 0xc9, 0xcc, 0x4b, 0xd7, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b,
|
||||
0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0xc8, 0x48, 0x89, 0xa6, 0xa4, 0x26, 0x67, 0xe6, 0x26,
|
||||
0xe6, 0xe8, 0x43, 0x69, 0x88, 0x12, 0x29, 0x31, 0x24, 0xcd, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x50,
|
||||
0x71, 0xdd, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0xf4, 0xfc, 0xf4,
|
||||
0x7c, 0x7d, 0xb0, 0x70, 0x52, 0x69, 0x1a, 0x98, 0x07, 0xe6, 0x80, 0x59, 0x10, 0xe5, 0x4a, 0xbe,
|
||||
0x5c, 0x7c, 0x4e, 0x89, 0x39, 0x89, 0x79, 0xc9, 0xa9, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25,
|
||||
0x42, 0x9a, 0x5c, 0xec, 0xfe, 0xe5, 0x79, 0xa9, 0x45, 0x9e, 0x2e, 0x12, 0x8c, 0x0a, 0x8c, 0x1a,
|
||||
0x3c, 0x4e, 0xfc, 0x27, 0xee, 0xc9, 0x33, 0xdc, 0xba, 0x27, 0x0f, 0x13, 0x0e, 0x82, 0x31, 0x84,
|
||||
0x04, 0xb8, 0x98, 0x43, 0x42, 0x7c, 0x24, 0x98, 0x14, 0x18, 0x35, 0x78, 0x83, 0x40, 0x4c, 0xa5,
|
||||
0x32, 0x2e, 0x7e, 0xb8, 0x71, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x42, 0x5a, 0x5c, 0xec, 0x50,
|
||||
0x21, 0xb0, 0x79, 0xdc, 0x46, 0x02, 0x7a, 0x30, 0x9f, 0xb8, 0x40, 0xe8, 0x20, 0x98, 0x02, 0x21,
|
||||
0x73, 0x2e, 0x1e, 0x9f, 0xfc, 0xe4, 0x6c, 0x47, 0x88, 0xd7, 0x8a, 0x25, 0x98, 0x14, 0x98, 0x35,
|
||||
0xb8, 0x8d, 0x84, 0xf5, 0x10, 0x7e, 0xd5, 0x83, 0xca, 0x05, 0xa1, 0x28, 0x34, 0x0a, 0xe0, 0xe2,
|
||||
0x72, 0x84, 0xab, 0x11, 0x72, 0x82, 0x5b, 0x29, 0x24, 0x85, 0xac, 0x17, 0xd5, 0xa7, 0x52, 0xd2,
|
||||
0x58, 0xe5, 0x20, 0xce, 0x76, 0x72, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x1b,
|
||||
0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x1b, 0x29, 0x74,
|
||||
0xf3, 0x8a, 0x0b, 0x92, 0x93, 0x75, 0x53, 0x52, 0xcb, 0xf4, 0xf3, 0x52, 0xf3, 0xd3, 0x8a, 0x75,
|
||||
0x21, 0x61, 0x8b, 0x30, 0x32, 0x89, 0x0d, 0x2c, 0x62, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x0c,
|
||||
0x45, 0x3c, 0x0a, 0xe8, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// AccountingClient is the client API for Accounting service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type AccountingClient interface {
|
||||
Balance(ctx context.Context, in *BalanceRequest, opts ...grpc.CallOption) (*BalanceResponse, error)
|
||||
}
|
||||
|
||||
type accountingClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewAccountingClient(cc *grpc.ClientConn) AccountingClient {
|
||||
return &accountingClient{cc}
|
||||
}
|
||||
|
||||
func (c *accountingClient) Balance(ctx context.Context, in *BalanceRequest, opts ...grpc.CallOption) (*BalanceResponse, error) {
|
||||
out := new(BalanceResponse)
|
||||
err := c.cc.Invoke(ctx, "/accounting.Accounting/Balance", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AccountingServer is the server API for Accounting service.
|
||||
type AccountingServer interface {
|
||||
Balance(context.Context, *BalanceRequest) (*BalanceResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedAccountingServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedAccountingServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedAccountingServer) Balance(ctx context.Context, req *BalanceRequest) (*BalanceResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Balance not implemented")
|
||||
}
|
||||
|
||||
func RegisterAccountingServer(s *grpc.Server, srv AccountingServer) {
|
||||
s.RegisterService(&_Accounting_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Accounting_Balance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(BalanceRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AccountingServer).Balance(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/accounting.Accounting/Balance",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AccountingServer).Balance(ctx, req.(*BalanceRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Accounting_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "accounting.Accounting",
|
||||
HandlerType: (*AccountingServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Balance",
|
||||
Handler: _Accounting_Balance_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "accounting/service.proto",
|
||||
}
|
||||
|
||||
func (m *BalanceRequest) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *BalanceRequest) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *BalanceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.XXX_unrecognized != nil {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if m.TTL != 0 {
|
||||
i = encodeVarintService(dAtA, i, uint64(m.TTL))
|
||||
i--
|
||||
dAtA[i] = 0x10
|
||||
}
|
||||
{
|
||||
size := m.OwnerID.Size()
|
||||
i -= size
|
||||
if _, err := m.OwnerID.MarshalTo(dAtA[i:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i = encodeVarintService(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *BalanceResponse) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *BalanceResponse) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *BalanceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.XXX_unrecognized != nil {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if len(m.LockAccounts) > 0 {
|
||||
for iNdEx := len(m.LockAccounts) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
size, err := m.LockAccounts[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintService(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
}
|
||||
if m.Balance != nil {
|
||||
{
|
||||
size, err := m.Balance.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintService(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintService(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovService(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return base
|
||||
}
|
||||
func (m *BalanceRequest) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
l = m.OwnerID.Size()
|
||||
n += 1 + l + sovService(uint64(l))
|
||||
if m.TTL != 0 {
|
||||
n += 1 + sovService(uint64(m.TTL))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *BalanceResponse) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if m.Balance != nil {
|
||||
l = m.Balance.Size()
|
||||
n += 1 + l + sovService(uint64(l))
|
||||
}
|
||||
if len(m.LockAccounts) > 0 {
|
||||
for _, e := range m.LockAccounts {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovService(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovService(x uint64) (n int) {
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozService(x uint64) (n int) {
|
||||
return sovService(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *BalanceRequest) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowService
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: BalanceRequest: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: BalanceRequest: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field OwnerID", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowService
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthService
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthService
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := m.OwnerID.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
|
||||
}
|
||||
m.TTL = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowService
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.TTL |= uint32(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipService(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthService
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthService
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *BalanceResponse) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowService
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: BalanceResponse: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: BalanceResponse: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Balance", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowService
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthService
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthService
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.Balance == nil {
|
||||
m.Balance = &decimal.Decimal{}
|
||||
}
|
||||
if err := m.Balance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field LockAccounts", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowService
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthService
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthService
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.LockAccounts = append(m.LockAccounts, &Account{})
|
||||
if err := m.LockAccounts[len(m.LockAccounts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipService(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthService
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthService
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipService(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowService
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowService
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if dAtA[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowService
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthService
|
||||
}
|
||||
iNdEx += length
|
||||
case 3:
|
||||
depth++
|
||||
case 4:
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupService
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthService
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthService = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowService = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupService = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
23
accounting/service.proto
Normal file
23
accounting/service.proto
Normal file
|
@ -0,0 +1,23 @@
|
|||
syntax = "proto3";
|
||||
package accounting;
|
||||
option go_package = "github.com/nspcc-dev/neofs-proto/accounting";
|
||||
|
||||
import "decimal/decimal.proto";
|
||||
import "accounting/types.proto";
|
||||
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
|
||||
|
||||
option (gogoproto.stable_marshaler_all) = true;
|
||||
|
||||
service Accounting {
|
||||
rpc Balance(BalanceRequest) returns (BalanceResponse);
|
||||
}
|
||||
|
||||
message BalanceRequest {
|
||||
bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
|
||||
uint32 TTL = 2;
|
||||
}
|
||||
|
||||
message BalanceResponse {
|
||||
decimal.Decimal Balance = 1;
|
||||
repeated Account LockAccounts = 2;
|
||||
}
|
353
accounting/types.go
Normal file
353
accounting/types.go
Normal file
|
@ -0,0 +1,353 @@
|
|||
package accounting
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
|
||||
"github.com/mr-tron/base58"
|
||||
crypto "github.com/nspcc-dev/neofs-crypto"
|
||||
"github.com/nspcc-dev/neofs-proto/chain"
|
||||
"github.com/nspcc-dev/neofs-proto/decimal"
|
||||
"github.com/nspcc-dev/neofs-proto/internal"
|
||||
"github.com/nspcc-dev/neofs-proto/refs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type (
|
||||
// Cheque structure that describes a user request for withdrawal of funds.
|
||||
Cheque struct {
|
||||
ID ChequeID
|
||||
Owner refs.OwnerID
|
||||
Amount *decimal.Decimal
|
||||
Height uint64
|
||||
Signatures []ChequeSignature
|
||||
}
|
||||
|
||||
// BalanceReceiver interface that is used to retrieve user balance by address.
|
||||
BalanceReceiver interface {
|
||||
Balance(accountAddress string) (*Account, error)
|
||||
}
|
||||
|
||||
// ChequeID is identifier of user request for withdrawal of funds.
|
||||
ChequeID string
|
||||
|
||||
// CID type alias.
|
||||
CID = refs.CID
|
||||
|
||||
// SGID type alias.
|
||||
SGID = refs.SGID
|
||||
|
||||
// ChequeSignature contains public key and hash, and is used to verify signatures.
|
||||
ChequeSignature struct {
|
||||
Key *ecdsa.PublicKey
|
||||
Hash []byte
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
// ErrWrongSignature is raised when wrong signature is passed.
|
||||
ErrWrongSignature = internal.Error("wrong signature")
|
||||
|
||||
// ErrWrongPublicKey is raised when wrong public key is passed.
|
||||
ErrWrongPublicKey = internal.Error("wrong public key")
|
||||
|
||||
// ErrWrongChequeData is raised when passed bytes cannot not be parsed as valid Cheque.
|
||||
ErrWrongChequeData = internal.Error("wrong cheque data")
|
||||
|
||||
// ErrInvalidLength is raised when passed bytes cannot not be parsed as valid ChequeID.
|
||||
ErrInvalidLength = internal.Error("invalid length")
|
||||
|
||||
u16size = 2
|
||||
u64size = 8
|
||||
|
||||
signaturesOffset = chain.AddressLength + refs.OwnerIDSize + u64size + u64size
|
||||
)
|
||||
|
||||
// NewChequeID generates valid random ChequeID using crypto/rand.Reader.
|
||||
func NewChequeID() (ChequeID, error) {
|
||||
d := make([]byte, chain.AddressLength)
|
||||
if _, err := rand.Read(d); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
id := base58.Encode(d)
|
||||
|
||||
return ChequeID(id), nil
|
||||
}
|
||||
|
||||
// String returns string representation of ChequeID.
|
||||
func (b ChequeID) String() string { return string(b) }
|
||||
|
||||
// Empty returns true, if ChequeID is empty.
|
||||
func (b ChequeID) Empty() bool { return len(b) == 0 }
|
||||
|
||||
// Valid validates ChequeID.
|
||||
func (b ChequeID) Valid() bool {
|
||||
d, err := base58.Decode(string(b))
|
||||
return err == nil && len(d) == chain.AddressLength
|
||||
}
|
||||
|
||||
// Bytes returns bytes representation of ChequeID.
|
||||
func (b ChequeID) Bytes() []byte {
|
||||
d, err := base58.Decode(string(b))
|
||||
if err != nil {
|
||||
return make([]byte, chain.AddressLength)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// Equal checks that current ChequeID is equal to passed ChequeID.
|
||||
func (b ChequeID) Equal(b2 ChequeID) bool {
|
||||
return b.Valid() && b2.Valid() && string(b) == string(b2)
|
||||
}
|
||||
|
||||
// Unmarshal tries to parse []byte into valid ChequeID.
|
||||
func (b *ChequeID) Unmarshal(data []byte) error {
|
||||
*b = ChequeID(base58.Encode(data))
|
||||
if !b.Valid() {
|
||||
return ErrInvalidLength
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Size returns size (chain.AddressLength).
|
||||
func (b ChequeID) Size() int {
|
||||
return chain.AddressLength
|
||||
}
|
||||
|
||||
// MarshalTo tries to marshal ChequeID into passed bytes and returns
|
||||
// count of copied bytes or error, if bytes len is not enough to contain ChequeID.
|
||||
func (b ChequeID) MarshalTo(data []byte) (int, error) {
|
||||
if len(data) < chain.AddressLength {
|
||||
return 0, ErrInvalidLength
|
||||
}
|
||||
return copy(data, b.Bytes()), nil
|
||||
}
|
||||
|
||||
// Equals checks that m and tx are valid and equal Tx values.
|
||||
func (m Tx) Equals(tx Tx) bool {
|
||||
return m.From == tx.From &&
|
||||
m.To == tx.To &&
|
||||
m.Type == tx.Type &&
|
||||
m.Amount == tx.Amount
|
||||
}
|
||||
|
||||
// Verify validates current Cheque and Signatures that are generated for current Cheque.
|
||||
func (b Cheque) Verify() error {
|
||||
data := b.marshalBody()
|
||||
for i, sign := range b.Signatures {
|
||||
if err := crypto.VerifyRFC6979(sign.Key, data, sign.Hash); err != nil {
|
||||
return errors.Wrapf(ErrWrongSignature, "item #%d: %s", i, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sign is used to sign current Cheque and stores result inside b.Signatures.
|
||||
func (b *Cheque) Sign(key *ecdsa.PrivateKey) error {
|
||||
hash, err := crypto.SignRFC6979(key, b.marshalBody())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.Signatures = append(b.Signatures, ChequeSignature{
|
||||
Key: &key.PublicKey,
|
||||
Hash: hash,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Cheque) marshalBody() []byte {
|
||||
buf := make([]byte, signaturesOffset)
|
||||
|
||||
var offset int
|
||||
|
||||
offset += copy(buf, b.ID.Bytes())
|
||||
offset += copy(buf[offset:], b.Owner.Bytes())
|
||||
|
||||
binary.BigEndian.PutUint64(buf[offset:], uint64(b.Amount.Value))
|
||||
offset += u64size
|
||||
|
||||
binary.BigEndian.PutUint64(buf[offset:], b.Height)
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
func (b *Cheque) unmarshalBody(buf []byte) error {
|
||||
var offset int
|
||||
|
||||
if len(buf) < signaturesOffset {
|
||||
return ErrWrongChequeData
|
||||
}
|
||||
|
||||
{ // unmarshal UUID
|
||||
if err := b.ID.Unmarshal(buf[offset : offset+chain.AddressLength]); err != nil {
|
||||
return err
|
||||
}
|
||||
offset += chain.AddressLength
|
||||
}
|
||||
|
||||
{ // unmarshal OwnerID
|
||||
if err := b.Owner.Unmarshal(buf[offset : offset+refs.OwnerIDSize]); err != nil {
|
||||
return err
|
||||
}
|
||||
offset += refs.OwnerIDSize
|
||||
}
|
||||
|
||||
{ // unmarshal amount
|
||||
amount := int64(binary.BigEndian.Uint64(buf[offset:]))
|
||||
b.Amount = decimal.New(amount)
|
||||
offset += u64size
|
||||
}
|
||||
|
||||
{ // unmarshal height
|
||||
b.Height = binary.BigEndian.Uint64(buf[offset:])
|
||||
offset += u64size
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary is used to marshal Cheque into bytes.
|
||||
func (b Cheque) MarshalBinary() ([]byte, error) {
|
||||
var (
|
||||
count = len(b.Signatures)
|
||||
buf = make([]byte, b.Size())
|
||||
offset = copy(buf, b.marshalBody())
|
||||
)
|
||||
|
||||
binary.BigEndian.PutUint16(buf[offset:], uint16(count))
|
||||
offset += u16size
|
||||
|
||||
for _, sign := range b.Signatures {
|
||||
key := crypto.MarshalPublicKey(sign.Key)
|
||||
offset += copy(buf[offset:], key)
|
||||
offset += copy(buf[offset:], sign.Hash)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Size returns size of Cheque (count of bytes needs to store it).
|
||||
func (b Cheque) Size() int {
|
||||
return signaturesOffset + u16size +
|
||||
len(b.Signatures)*(crypto.PublicKeyCompressedSize+crypto.RFC6979SignatureSize)
|
||||
}
|
||||
|
||||
// UnmarshalBinary tries to parse []byte into valid Cheque.
|
||||
func (b *Cheque) UnmarshalBinary(buf []byte) error {
|
||||
if err := b.unmarshalBody(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
body := buf[:signaturesOffset]
|
||||
|
||||
count := int64(binary.BigEndian.Uint16(buf[signaturesOffset:]))
|
||||
offset := signaturesOffset + u16size
|
||||
|
||||
if ln := count * int64(crypto.PublicKeyCompressedSize+crypto.RFC6979SignatureSize); ln > int64(len(buf[offset:])) {
|
||||
return ErrWrongChequeData
|
||||
}
|
||||
|
||||
for i := int64(0); i < count; i++ {
|
||||
sign := ChequeSignature{
|
||||
Key: crypto.UnmarshalPublicKey(buf[offset : offset+crypto.PublicKeyCompressedSize]),
|
||||
Hash: make([]byte, crypto.RFC6979SignatureSize),
|
||||
}
|
||||
|
||||
offset += crypto.PublicKeyCompressedSize
|
||||
if sign.Key == nil {
|
||||
return errors.Wrapf(ErrWrongPublicKey, "item #%d", i)
|
||||
}
|
||||
|
||||
offset += copy(sign.Hash, buf[offset:offset+crypto.RFC6979SignatureSize])
|
||||
if err := crypto.VerifyRFC6979(sign.Key, body, sign.Hash); err != nil {
|
||||
return errors.Wrapf(ErrWrongSignature, "item #%d: %s (offset=%d, len=%d)", i, err.Error(), offset, len(sign.Hash))
|
||||
}
|
||||
|
||||
b.Signatures = append(b.Signatures, sign)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErrNotEnoughFunds generates error using address and amounts.
|
||||
func ErrNotEnoughFunds(addr string, needed, residue *decimal.Decimal) error {
|
||||
return errors.Errorf("not enough funds (requested=%s, residue=%s, addr=%s", needed, residue, addr)
|
||||
}
|
||||
|
||||
func (m *Account) hasLockAcc(addr string) bool {
|
||||
for i := range m.LockAccounts {
|
||||
if m.LockAccounts[i].Address == addr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ValidateLock checks that account can be locked.
|
||||
func (m *Account) ValidateLock() error {
|
||||
switch {
|
||||
case m.Address == "":
|
||||
return ErrEmptyAddress
|
||||
case m.ParentAddress == "":
|
||||
return ErrEmptyParentAddress
|
||||
case m.LockTarget == nil:
|
||||
return ErrEmptyLockTarget
|
||||
}
|
||||
|
||||
switch v := m.LockTarget.Target.(type) {
|
||||
case *LockTarget_WithdrawTarget:
|
||||
if v.WithdrawTarget.Cheque != m.Address {
|
||||
return errors.Errorf("wrong cheque ID: expected %s, has %s", m.Address, v.WithdrawTarget.Cheque)
|
||||
}
|
||||
case *LockTarget_ContainerCreateTarget:
|
||||
switch {
|
||||
case v.ContainerCreateTarget.CID.Empty():
|
||||
return ErrEmptyContainerID
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanLock checks possibility to lock funds.
|
||||
func (m *Account) CanLock(lockAcc *Account) error {
|
||||
switch {
|
||||
case m.ActiveFunds.LT(lockAcc.ActiveFunds):
|
||||
return ErrNotEnoughFunds(lockAcc.ParentAddress, lockAcc.ActiveFunds, m.ActiveFunds)
|
||||
case m.hasLockAcc(lockAcc.Address):
|
||||
return errors.Errorf("could not lock account(%s) funds: duplicating lock(%s)", m.Address, lockAcc.Address)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// LockForWithdraw checks that account contains locked funds by passed ChequeID.
|
||||
func (m *Account) LockForWithdraw(chequeID string) bool {
|
||||
switch v := m.LockTarget.Target.(type) {
|
||||
case *LockTarget_WithdrawTarget:
|
||||
return v.WithdrawTarget.Cheque == chequeID
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LockForContainerCreate checks that account contains locked funds for container creation.
|
||||
func (m *Account) LockForContainerCreate(cid refs.CID) bool {
|
||||
switch v := m.LockTarget.Target.(type) {
|
||||
case *LockTarget_ContainerCreateTarget:
|
||||
return v.ContainerCreateTarget.CID.Equal(cid)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Equal checks that current Settlement is equal to passed Settlement.
|
||||
func (m *Settlement) Equal(s *Settlement) bool {
|
||||
if s == nil || m.Epoch != s.Epoch || len(m.Transactions) != len(s.Transactions) {
|
||||
return false
|
||||
}
|
||||
return len(m.Transactions) == 0 || reflect.DeepEqual(m.Transactions, s.Transactions)
|
||||
}
|
3905
accounting/types.pb.go
Normal file
3905
accounting/types.pb.go
Normal file
File diff suppressed because it is too large
Load diff
106
accounting/types.proto
Normal file
106
accounting/types.proto
Normal file
|
@ -0,0 +1,106 @@
|
|||
syntax = "proto3";
|
||||
package accounting;
|
||||
option go_package = "github.com/nspcc-dev/neofs-proto/accounting";
|
||||
|
||||
import "decimal/decimal.proto";
|
||||
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
|
||||
|
||||
option (gogoproto.stable_marshaler_all) = true;
|
||||
|
||||
// Snapshot accounting messages
|
||||
message Account {
|
||||
bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
|
||||
string Address = 2;
|
||||
string ParentAddress = 3;
|
||||
decimal.Decimal ActiveFunds = 4;
|
||||
Lifetime Lifetime = 5 [(gogoproto.nullable) = false];
|
||||
LockTarget LockTarget = 6;
|
||||
repeated Account LockAccounts = 7;
|
||||
}
|
||||
|
||||
message LockTarget {
|
||||
oneof Target {
|
||||
WithdrawTarget WithdrawTarget = 1;
|
||||
ContainerCreateTarget ContainerCreateTarget = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot balance messages
|
||||
message Balances {
|
||||
repeated Account Accounts = 1 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// PayIn / PayOut messages
|
||||
message PayIO {
|
||||
uint64 BlockID = 1;
|
||||
repeated Tx Transactions = 2 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// Clearing messages
|
||||
message Clearing {
|
||||
repeated Tx Transactions = 1 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// Clearing messages
|
||||
message Withdraw {
|
||||
string ID = 1;
|
||||
uint64 Epoch = 2;
|
||||
Tx Transaction = 3;
|
||||
}
|
||||
|
||||
// Lifetime of locks
|
||||
message Lifetime {
|
||||
enum Unit {
|
||||
Unlimited = 0;
|
||||
NeoFSEpoch = 1;
|
||||
NeoBlock = 2;
|
||||
}
|
||||
|
||||
Unit unit = 1 [(gogoproto.customname) = "Unit"];
|
||||
int64 Value = 2;
|
||||
}
|
||||
|
||||
// Transaction messages
|
||||
message Tx {
|
||||
enum Type {
|
||||
Unknown = 0;
|
||||
Withdraw = 1;
|
||||
PayIO = 2;
|
||||
Inner = 3;
|
||||
}
|
||||
|
||||
Type type = 1 [(gogoproto.customname) = "Type"];
|
||||
string From = 2;
|
||||
string To = 3;
|
||||
decimal.Decimal Amount = 4;
|
||||
bytes PublicKeys = 5; // of sender
|
||||
}
|
||||
|
||||
message Settlement {
|
||||
message Receiver {
|
||||
string To = 1;
|
||||
decimal.Decimal Amount = 2;
|
||||
}
|
||||
|
||||
message Container {
|
||||
bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false];
|
||||
repeated bytes SGIDs = 2 [(gogoproto.customtype) = "SGID", (gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
message Tx {
|
||||
string From = 1;
|
||||
Container Container = 2 [(gogoproto.nullable) = false];
|
||||
repeated Receiver Receivers = 3 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
uint64 Epoch = 1;
|
||||
repeated Tx Transactions = 2;
|
||||
}
|
||||
|
||||
message ContainerCreateTarget {
|
||||
bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
message WithdrawTarget {
|
||||
string Cheque = 1;
|
||||
}
|
84
accounting/types_test.go
Normal file
84
accounting/types_test.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package accounting
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/mr-tron/base58"
|
||||
"github.com/nspcc-dev/neofs-crypto/test"
|
||||
"github.com/nspcc-dev/neofs-proto/chain"
|
||||
"github.com/nspcc-dev/neofs-proto/decimal"
|
||||
"github.com/nspcc-dev/neofs-proto/refs"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCheque(t *testing.T) {
|
||||
t.Run("new/valid", func(t *testing.T) {
|
||||
id, err := NewChequeID()
|
||||
require.NoError(t, err)
|
||||
require.True(t, id.Valid())
|
||||
|
||||
d := make([]byte, chain.AddressLength+1)
|
||||
|
||||
// expected size + 1 byte
|
||||
str := base58.Encode(d)
|
||||
require.False(t, ChequeID(str).Valid())
|
||||
|
||||
// expected size - 1 byte
|
||||
str = base58.Encode(d[:len(d)-2])
|
||||
require.False(t, ChequeID(str).Valid())
|
||||
|
||||
// wrong encoding
|
||||
d = d[:len(d)-1] // normal size
|
||||
require.False(t, ChequeID(string(d)).Valid())
|
||||
})
|
||||
|
||||
t.Run("marshal/unmarshal", func(t *testing.T) {
|
||||
var b2 = new(Cheque)
|
||||
|
||||
key1 := test.DecodeKey(0)
|
||||
key2 := test.DecodeKey(1)
|
||||
|
||||
id, err := NewChequeID()
|
||||
require.NoError(t, err)
|
||||
|
||||
owner, err := refs.NewOwnerID(&key1.PublicKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
b1 := &Cheque{
|
||||
ID: id,
|
||||
Owner: owner,
|
||||
Height: 100,
|
||||
Amount: decimal.NewGAS(100),
|
||||
}
|
||||
|
||||
require.NoError(t, b1.Sign(key1))
|
||||
require.NoError(t, b1.Sign(key2))
|
||||
|
||||
data, err := b1.MarshalBinary()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, data, b1.Size())
|
||||
require.NoError(t, b2.UnmarshalBinary(data))
|
||||
require.Equal(t, b1, b2)
|
||||
|
||||
require.NoError(t, b1.Verify())
|
||||
require.NoError(t, b2.Verify())
|
||||
})
|
||||
|
||||
t.Run("example from SC", func(t *testing.T) {
|
||||
var pathToCheque = "fixtures/cheque_data"
|
||||
expect, err := ioutil.ReadFile(pathToCheque)
|
||||
require.NoError(t, err)
|
||||
|
||||
var cheque Cheque
|
||||
require.NoError(t, cheque.UnmarshalBinary(expect))
|
||||
|
||||
actual, err := cheque.MarshalBinary()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expect, actual)
|
||||
|
||||
require.NoError(t, cheque.Verify())
|
||||
})
|
||||
}
|
53
accounting/withdraw.go
Normal file
53
accounting/withdraw.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package accounting
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/nspcc-dev/neofs-proto/refs"
|
||||
)
|
||||
|
||||
type (
|
||||
// MessageID type alias.
|
||||
MessageID = refs.MessageID
|
||||
)
|
||||
|
||||
// SetTTL sets ttl to GetRequest to satisfy TTLRequest interface.
|
||||
func (m *GetRequest) SetTTL(v uint32) { m.TTL = v }
|
||||
|
||||
// SetTTL sets ttl to PutRequest to satisfy TTLRequest interface.
|
||||
func (m *PutRequest) SetTTL(v uint32) { m.TTL = v }
|
||||
|
||||
// SetTTL sets ttl to ListRequest to satisfy TTLRequest interface.
|
||||
func (m *ListRequest) SetTTL(v uint32) { m.TTL = v }
|
||||
|
||||
// SetTTL sets ttl to DeleteRequest to satisfy TTLRequest interface.
|
||||
func (m *DeleteRequest) SetTTL(v uint32) { m.TTL = v }
|
||||
|
||||
// SetSignature sets signature to PutRequest to satisfy SignedRequest interface.
|
||||
func (m *PutRequest) SetSignature(v []byte) { m.Signature = v }
|
||||
|
||||
// SetSignature sets signature to DeleteRequest to satisfy SignedRequest interface.
|
||||
func (m *DeleteRequest) SetSignature(v []byte) { m.Signature = v }
|
||||
|
||||
// PrepareData prepares bytes representation of PutRequest to satisfy SignedRequest interface.
|
||||
func (m *PutRequest) PrepareData() ([]byte, error) {
|
||||
var offset int
|
||||
// MessageID-len + OwnerID-len + Amount + Height
|
||||
buf := make([]byte, refs.UUIDSize+refs.OwnerIDSize+binary.MaxVarintLen64+binary.MaxVarintLen64)
|
||||
offset += copy(buf[offset:], m.MessageID.Bytes())
|
||||
offset += copy(buf[offset:], m.OwnerID.Bytes())
|
||||
offset += binary.PutVarint(buf[offset:], m.Amount.Value)
|
||||
binary.PutUvarint(buf[offset:], m.Height)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// PrepareData prepares bytes representation of DeleteRequest to satisfy SignedRequest interface.
|
||||
func (m *DeleteRequest) PrepareData() ([]byte, error) {
|
||||
var offset int
|
||||
// ID-len + OwnerID-len + MessageID-len
|
||||
buf := make([]byte, refs.UUIDSize+refs.OwnerIDSize+refs.UUIDSize)
|
||||
offset += copy(buf[offset:], m.ID.Bytes())
|
||||
offset += copy(buf[offset:], m.OwnerID.Bytes())
|
||||
copy(buf[offset:], m.MessageID.Bytes())
|
||||
return buf, nil
|
||||
}
|
2641
accounting/withdraw.pb.go
Normal file
2641
accounting/withdraw.pb.go
Normal file
File diff suppressed because it is too large
Load diff
61
accounting/withdraw.proto
Normal file
61
accounting/withdraw.proto
Normal file
|
@ -0,0 +1,61 @@
|
|||
syntax = "proto3";
|
||||
package accounting;
|
||||
option go_package = "github.com/nspcc-dev/neofs-proto/accounting";
|
||||
|
||||
import "decimal/decimal.proto";
|
||||
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
|
||||
|
||||
option (gogoproto.stable_marshaler_all) = true;
|
||||
|
||||
service Withdraw {
|
||||
rpc Get(GetRequest) returns (GetResponse);
|
||||
rpc Put(PutRequest) returns (PutResponse);
|
||||
rpc List(ListRequest) returns (ListResponse);
|
||||
rpc Delete(DeleteRequest) returns (DeleteResponse);
|
||||
}
|
||||
|
||||
message Item {
|
||||
bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false];
|
||||
bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
|
||||
decimal.Decimal Amount = 3;
|
||||
uint64 Height = 4;
|
||||
bytes Payload = 5;
|
||||
}
|
||||
|
||||
message GetRequest {
|
||||
bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false];
|
||||
bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
|
||||
uint32 TTL = 3;
|
||||
}
|
||||
message GetResponse {
|
||||
Item Withdraw = 1;
|
||||
}
|
||||
|
||||
message PutRequest {
|
||||
bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
|
||||
decimal.Decimal Amount = 2;
|
||||
uint64 Height = 3;
|
||||
bytes MessageID = 4 [(gogoproto.customtype) = "MessageID", (gogoproto.nullable) = false];
|
||||
bytes Signature = 5;
|
||||
uint32 TTL = 6;
|
||||
}
|
||||
message PutResponse {
|
||||
bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
message ListRequest {
|
||||
bytes OwnerID = 1 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
|
||||
uint32 TTL = 2;
|
||||
}
|
||||
message ListResponse {
|
||||
repeated Item Items = 1;
|
||||
}
|
||||
|
||||
message DeleteRequest {
|
||||
bytes ID = 1 [(gogoproto.customtype) = "ChequeID", (gogoproto.nullable) = false];
|
||||
bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
|
||||
bytes MessageID = 3 [(gogoproto.customtype) = "MessageID", (gogoproto.nullable) = false];
|
||||
bytes Signature = 4;
|
||||
uint32 TTL = 5;
|
||||
}
|
||||
message DeleteResponse {}
|
Loading…
Add table
Add a link
Reference in a new issue