Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
M
mybee
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
vicotor
mybee
Commits
1de167ea
Unverified
Commit
1de167ea
authored
Mar 17, 2021
by
Rodrigo Q. Saramago
Committed by
GitHub
Mar 17, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Soc package refactor (#1394)
parent
99aaaa79
Changes
14
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
567 additions
and
356 deletions
+567
-356
bzz_test.go
pkg/api/bzz_test.go
+1
-3
feed.go
pkg/api/feed.go
+3
-5
feed_test.go
pkg/api/feed_test.go
+7
-29
soc.go
pkg/api/soc.go
+13
-5
soc_test.go
pkg/api/soc_test.go
+21
-73
feed.go
pkg/feeds/feed.go
+8
-7
getter.go
pkg/feeds/getter.go
+1
-1
putter.go
pkg/feeds/putter.go
+2
-1
export_test.go
pkg/soc/export_test.go
+18
-2
soc.go
pkg/soc/soc.go
+95
-154
soc_test.go
pkg/soc/soc_test.go
+233
-36
soc.go
pkg/soc/testing/soc.go
+72
-0
validator.go
pkg/soc/validator.go
+1
-1
validator_test.go
pkg/soc/validator_test.go
+92
-39
No files found.
pkg/api/bzz_test.go
View file @
1de167ea
...
...
@@ -84,7 +84,6 @@ func TestBzz(t *testing.T) {
}
fileMetadataReference
,
err
:=
pipeWriteAll
(
bytes
.
NewReader
(
fileMetadataBytes
),
int64
(
len
(
fileMetadataBytes
)))
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
...
...
@@ -171,7 +170,6 @@ func TestBzz(t *testing.T) {
}),
)
})
}
func
TestFeedIndirection
(
t
*
testing
.
T
)
{
...
...
@@ -219,7 +217,7 @@ func TestFeedIndirection(t *testing.T) {
// called from the bzz endpoint. then call the bzz endpoint with
// the pregenerated feed root manifest hash
feedUpdate
,
_
:=
toChunk
(
121212
,
resp
.
Reference
.
Bytes
())
feedUpdate
:=
toChunk
(
t
,
121212
,
resp
.
Reference
.
Bytes
())
var
(
feedChunkAddr
=
swarm
.
MustParseHexAddress
(
"891a1d1c8436c792d02fc2e8883fef7ab387eaeaacd25aa9f518be7be7856d54"
)
...
...
pkg/api/feed.go
View file @
1de167ea
...
...
@@ -29,9 +29,7 @@ const (
feedMetadataEntryType
=
"swarm-feed-type"
)
var
(
errInvalidFeedUpdate
=
errors
.
New
(
"invalid feed update"
)
)
var
errInvalidFeedUpdate
=
errors
.
New
(
"invalid feed update"
)
type
feedReferenceResponse
struct
{
Reference
swarm
.
Address
`json:"reference"`
...
...
@@ -176,12 +174,12 @@ func (s *server) feedPostHandler(w http.ResponseWriter, r *http.Request) {
}
func
parseFeedUpdate
(
ch
swarm
.
Chunk
)
(
swarm
.
Address
,
int64
,
error
)
{
s
ch
,
err
:=
soc
.
FromChunk
(
ch
)
s
,
err
:=
soc
.
FromChunk
(
ch
)
if
err
!=
nil
{
return
swarm
.
ZeroAddress
,
0
,
fmt
.
Errorf
(
"soc unmarshal: %w"
,
err
)
}
update
:=
s
ch
.
Chunk
.
Data
()
update
:=
s
.
WrappedChunk
()
.
Data
()
// split the timestamp and reference
// possible values right now:
// unencrypted ref: span+timestamp+ref => 8+8+32=48
...
...
pkg/api/feed_test.go
View file @
1de167ea
...
...
@@ -16,15 +16,13 @@ import (
"testing"
"github.com/ethersphere/bee/pkg/api"
"github.com/ethersphere/bee/pkg/cac"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/feeds"
"github.com/ethersphere/bee/pkg/file/loadsave"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/manifest"
"github.com/ethersphere/bee/pkg/soc
"
testingsoc
"github.com/ethersphere/bee/pkg/soc/testing
"
statestore
"github.com/ethersphere/bee/pkg/statestore/mock"
"github.com/ethersphere/bee/pkg/storage"
"github.com/ethersphere/bee/pkg/storage/mock"
...
...
@@ -84,7 +82,7 @@ func TestFeed_Get(t *testing.T) {
t
.
Run
(
"with at"
,
func
(
t
*
testing
.
T
)
{
var
(
timestamp
=
int64
(
12121212
)
ch
,
_
=
toChunk
(
uint64
(
timestamp
),
expReference
.
Bytes
())
ch
=
toChunk
(
t
,
uint64
(
timestamp
),
expReference
.
Bytes
())
look
=
newMockLookup
(
12
,
0
,
ch
,
nil
,
&
id
{},
&
id
{})
factory
=
newMockFactory
(
look
)
idBytes
,
_
=
(
&
id
{})
.
MarshalBinary
()
...
...
@@ -115,7 +113,7 @@ func TestFeed_Get(t *testing.T) {
t
.
Run
(
"latest"
,
func
(
t
*
testing
.
T
)
{
var
(
timestamp
=
int64
(
12121212
)
ch
,
_
=
toChunk
(
uint64
(
timestamp
),
expReference
.
Bytes
())
ch
=
toChunk
(
t
,
uint64
(
timestamp
),
expReference
.
Bytes
())
look
=
newMockLookup
(
-
1
,
2
,
ch
,
nil
,
&
id
{},
&
id
{})
factory
=
newMockFactory
(
look
)
idBytes
,
_
=
(
&
id
{})
.
MarshalBinary
()
...
...
@@ -237,36 +235,16 @@ func (l *mockLookup) At(_ context.Context, at, after int64) (swarm.Chunk, feeds.
return
nil
,
nil
,
nil
,
errors
.
New
(
"no feed update found"
)
}
func
toChunk
(
at
uint64
,
payload
[]
byte
)
(
swarm
.
Chunk
,
error
)
{
func
toChunk
(
t
*
testing
.
T
,
at
uint64
,
payload
[]
byte
)
swarm
.
Chunk
{
ts
:=
make
([]
byte
,
8
)
binary
.
BigEndian
.
PutUint64
(
ts
,
at
)
content
:=
append
(
ts
,
payload
...
)
ch
,
err
:=
cac
.
New
(
content
)
if
err
!=
nil
{
return
nil
,
err
}
id
:=
make
([]
byte
,
soc
.
IdSize
)
privKey
,
err
:=
crypto
.
GenerateSecp256k1Key
()
if
err
!=
nil
{
return
nil
,
err
}
signer
:=
crypto
.
NewDefaultSigner
(
privKey
)
sch
:=
soc
.
New
(
id
,
ch
)
if
err
!=
nil
{
return
nil
,
err
}
err
=
sch
.
AddSigner
(
signer
)
if
err
!=
nil
{
return
nil
,
err
}
return
sch
.
ToChunk
()
s
:=
testingsoc
.
GenerateMockSOC
(
t
,
content
)
return
s
.
Chunk
()
}
type
id
struct
{
}
type
id
struct
{}
func
(
i
*
id
)
MarshalBinary
()
([]
byte
,
error
)
{
return
[]
byte
(
"accd"
),
nil
...
...
pkg/api/soc.go
View file @
1de167ea
...
...
@@ -88,7 +88,15 @@ func (s *server) socUploadHandler(w http.ResponseWriter, r *http.Request) {
return
}
chunk
,
err
:=
soc
.
NewSignedChunk
(
id
,
ch
,
owner
,
sig
)
ss
,
err
:=
soc
.
NewSigned
(
id
,
ch
,
owner
,
sig
)
if
err
!=
nil
{
s
.
logger
.
Debugf
(
"soc upload: address soc error: %v"
,
err
)
s
.
logger
.
Error
(
"soc upload: address soc error"
)
jsonhttp
.
Unauthorized
(
w
,
"invalid address"
)
return
}
sch
,
err
:=
ss
.
Chunk
()
if
err
!=
nil
{
s
.
logger
.
Debugf
(
"soc upload: read chunk data error: %v"
,
err
)
s
.
logger
.
Error
(
"soc upload: read chunk data error"
)
...
...
@@ -96,7 +104,7 @@ func (s *server) socUploadHandler(w http.ResponseWriter, r *http.Request) {
return
}
if
!
soc
.
Valid
(
chunk
)
{
if
!
soc
.
Valid
(
sch
)
{
s
.
logger
.
Debugf
(
"soc upload: invalid chunk: %v"
,
err
)
s
.
logger
.
Error
(
"soc upload: invalid chunk"
)
jsonhttp
.
Unauthorized
(
w
,
"invalid chunk"
)
...
...
@@ -106,7 +114,7 @@ func (s *server) socUploadHandler(w http.ResponseWriter, r *http.Request) {
ctx
:=
r
.
Context
()
has
,
err
:=
s
.
storer
.
Has
(
ctx
,
chunk
.
Address
())
has
,
err
:=
s
.
storer
.
Has
(
ctx
,
sch
.
Address
())
if
err
!=
nil
{
s
.
logger
.
Debugf
(
"soc upload: store has: %v"
,
err
)
s
.
logger
.
Error
(
"soc upload: store has"
)
...
...
@@ -119,7 +127,7 @@ func (s *server) socUploadHandler(w http.ResponseWriter, r *http.Request) {
return
}
_
,
err
=
s
.
storer
.
Put
(
ctx
,
requestModePut
(
r
),
chunk
)
_
,
err
=
s
.
storer
.
Put
(
ctx
,
requestModePut
(
r
),
sch
)
if
err
!=
nil
{
s
.
logger
.
Debugf
(
"soc upload: chunk write error: %v"
,
err
)
s
.
logger
.
Error
(
"soc upload: chunk write error"
)
...
...
@@ -127,5 +135,5 @@ func (s *server) socUploadHandler(w http.ResponseWriter, r *http.Request) {
return
}
jsonhttp
.
Created
(
w
,
chunkAddressResponse
{
Reference
:
chunk
.
Address
()})
jsonhttp
.
Created
(
w
,
chunkAddressResponse
{
Reference
:
sch
.
Address
()})
}
pkg/api/soc_test.go
View file @
1de167ea
...
...
@@ -6,7 +6,6 @@ package api_test
import
(
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"io/ioutil"
...
...
@@ -14,19 +13,19 @@ import (
"testing"
"github.com/ethersphere/bee/pkg/api"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/soc"
testingsoc
"github.com/ethersphere/bee/pkg/soc/testing"
statestore
"github.com/ethersphere/bee/pkg/statestore/mock"
"github.com/ethersphere/bee/pkg/storage/mock"
"github.com/ethersphere/bee/pkg/swarm"
"github.com/ethersphere/bee/pkg/tags"
)
func
TestS
oc
(
t
*
testing
.
T
)
{
func
TestS
OC
(
t
*
testing
.
T
)
{
var
(
testData
=
[]
byte
(
"foo"
)
socResource
=
func
(
owner
,
id
,
sig
string
)
string
{
return
fmt
.
Sprintf
(
"/soc/%s/%s?sig=%s"
,
owner
,
id
,
sig
)
}
mockStatestore
=
statestore
.
NewStateStore
()
logger
=
logging
.
New
(
ioutil
.
Discard
,
0
)
...
...
@@ -37,7 +36,6 @@ func TestSoc(t *testing.T) {
Tags
:
tag
,
})
)
t
.
Run
(
"cmpty data"
,
func
(
t
*
testing
.
T
)
{
jsonhttptest
.
Request
(
t
,
client
,
http
.
MethodPost
,
socResource
(
"8d3766440f0d7b949a5e32995d09619a7f86e632"
,
"bb"
,
"cc"
),
http
.
StatusBadRequest
,
jsonhttptest
.
WithExpectedJSONResponse
(
jsonhttp
.
StatusResponse
{
...
...
@@ -75,16 +73,16 @@ func TestSoc(t *testing.T) {
})
t
.
Run
(
"signature invalid"
,
func
(
t
*
testing
.
T
)
{
s
,
owner
,
payload
:=
mockSoc
(
t
)
id
:=
make
([]
byte
,
soc
.
IdSize
)
s
:=
testingsoc
.
GenerateMockSOC
(
t
,
testData
)
// modify the sign
sig
:=
s
.
Signature
()
sig
:=
make
([]
byte
,
soc
.
SignatureSize
)
copy
(
sig
,
s
.
Signature
)
sig
[
12
]
=
0x98
sig
[
10
]
=
0x12
jsonhttptest
.
Request
(
t
,
client
,
http
.
MethodPost
,
socResource
(
hex
.
EncodeToString
(
owner
),
hex
.
EncodeToString
(
id
),
hex
.
EncodeToString
(
sig
)),
http
.
StatusUnauthorized
,
jsonhttptest
.
WithRequestBody
(
bytes
.
NewReader
(
payload
)),
jsonhttptest
.
Request
(
t
,
client
,
http
.
MethodPost
,
socResource
(
hex
.
EncodeToString
(
s
.
Owner
),
hex
.
EncodeToString
(
s
.
ID
),
hex
.
EncodeToString
(
sig
)),
http
.
StatusUnauthorized
,
jsonhttptest
.
WithRequestBody
(
bytes
.
NewReader
(
s
.
WrappedChunk
.
Data
()
)),
jsonhttptest
.
WithExpectedJSONResponse
(
jsonhttp
.
StatusResponse
{
Message
:
"invalid chunk"
,
Code
:
http
.
StatusUnauthorized
,
...
...
@@ -93,55 +91,39 @@ func TestSoc(t *testing.T) {
})
t
.
Run
(
"ok"
,
func
(
t
*
testing
.
T
)
{
s
,
owner
,
payload
:=
mockSoc
(
t
)
id
:=
make
([]
byte
,
soc
.
IdSize
)
s
:=
testingsoc
.
GenerateMockSOC
(
t
,
testData
)
sig
:=
s
.
Signature
()
addr
,
err
:=
s
.
Address
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
jsonhttptest
.
Request
(
t
,
client
,
http
.
MethodPost
,
socResource
(
hex
.
EncodeToString
(
owner
),
hex
.
EncodeToString
(
id
),
hex
.
EncodeToString
(
sig
)),
http
.
StatusCreated
,
jsonhttptest
.
WithRequestBody
(
bytes
.
NewReader
(
payload
)),
jsonhttptest
.
Request
(
t
,
client
,
http
.
MethodPost
,
socResource
(
hex
.
EncodeToString
(
s
.
Owner
),
hex
.
EncodeToString
(
s
.
ID
),
hex
.
EncodeToString
(
s
.
Signature
)),
http
.
StatusCreated
,
jsonhttptest
.
WithRequestBody
(
bytes
.
NewReader
(
s
.
WrappedChunk
.
Data
())),
jsonhttptest
.
WithExpectedJSONResponse
(
api
.
SocPostResponse
{
Reference
:
addr
,
Reference
:
s
.
Address
()
,
}),
)
// try to fetch the same chunk
rsrc
:=
fmt
.
Sprintf
(
"/chunks/"
+
addr
.
String
())
rsrc
:=
fmt
.
Sprintf
(
"/chunks/"
+
s
.
Address
()
.
String
())
resp
:=
request
(
t
,
client
,
http
.
MethodGet
,
rsrc
,
nil
,
http
.
StatusOK
)
data
,
err
:=
ioutil
.
ReadAll
(
resp
.
Body
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
ch
,
err
:=
s
.
ToChunk
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
!
bytes
.
Equal
(
ch
.
Data
(),
data
)
{
t
.
Fatal
(
"data retrieved doesnt match uploaded content"
)
if
!
bytes
.
Equal
(
s
.
Chunk
()
.
Data
(),
data
)
{
t
.
Fatal
(
"data retrieved doesn
'
t match uploaded content"
)
}
})
t
.
Run
(
"already exists"
,
func
(
t
*
testing
.
T
)
{
s
,
owner
,
payload
:=
mockSoc
(
t
)
id
:=
make
([]
byte
,
soc
.
IdSize
)
s
:=
testingsoc
.
GenerateMockSOC
(
t
,
testData
)
sig
:=
s
.
Signature
()
addr
,
err
:=
s
.
Address
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
jsonhttptest
.
Request
(
t
,
client
,
http
.
MethodPost
,
socResource
(
hex
.
EncodeToString
(
owner
),
hex
.
EncodeToString
(
id
),
hex
.
EncodeToString
(
sig
)),
http
.
StatusCreated
,
jsonhttptest
.
WithRequestBody
(
bytes
.
NewReader
(
payload
)),
jsonhttptest
.
Request
(
t
,
client
,
http
.
MethodPost
,
socResource
(
hex
.
EncodeToString
(
s
.
Owner
),
hex
.
EncodeToString
(
s
.
ID
),
hex
.
EncodeToString
(
s
.
Signature
)),
http
.
StatusCreated
,
jsonhttptest
.
WithRequestBody
(
bytes
.
NewReader
(
s
.
WrappedChunk
.
Data
())),
jsonhttptest
.
WithExpectedJSONResponse
(
api
.
SocPostResponse
{
Reference
:
addr
,
Reference
:
s
.
Address
()
,
}),
)
jsonhttptest
.
Request
(
t
,
client
,
http
.
MethodPost
,
socResource
(
hex
.
EncodeToString
(
owner
),
hex
.
EncodeToString
(
id
),
hex
.
EncodeToString
(
sig
)),
http
.
StatusConflict
,
jsonhttptest
.
WithRequestBody
(
bytes
.
NewReader
(
payload
)),
jsonhttptest
.
Request
(
t
,
client
,
http
.
MethodPost
,
socResource
(
hex
.
EncodeToString
(
s
.
Owner
),
hex
.
EncodeToString
(
s
.
ID
),
hex
.
EncodeToString
(
s
.
Signature
)),
http
.
StatusConflict
,
jsonhttptest
.
WithRequestBody
(
bytes
.
NewReader
(
s
.
WrappedChunk
.
Data
()
)),
jsonhttptest
.
WithExpectedJSONResponse
(
jsonhttp
.
StatusResponse
{
Message
:
"chunk already exists"
,
...
...
@@ -149,38 +131,4 @@ func TestSoc(t *testing.T) {
}),
)
})
}
// returns a valid, mocked SOC
func
mockSoc
(
t
*
testing
.
T
)
(
*
soc
.
Soc
,
[]
byte
,
[]
byte
)
{
// create a valid soc
id
:=
make
([]
byte
,
soc
.
IdSize
)
privKey
,
err
:=
crypto
.
GenerateSecp256k1Key
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
signer
:=
crypto
.
NewDefaultSigner
(
privKey
)
bmtHashOfFoo
:=
"2387e8e7d8a48c2a9339c97c1dc3461a9a7aa07e994c5cb8b38fd7c1b3e6ea48"
address
:=
swarm
.
MustParseHexAddress
(
bmtHashOfFoo
)
foo
:=
"foo"
fooLength
:=
len
(
foo
)
fooBytes
:=
make
([]
byte
,
8
+
fooLength
)
binary
.
LittleEndian
.
PutUint64
(
fooBytes
,
uint64
(
fooLength
))
copy
(
fooBytes
[
8
:
],
foo
)
ch
:=
swarm
.
NewChunk
(
address
,
fooBytes
)
sch
:=
soc
.
New
(
id
,
ch
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
err
=
sch
.
AddSigner
(
signer
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
_
,
_
=
sch
.
ToChunk
()
return
sch
,
sch
.
OwnerAddress
(),
ch
.
Data
()
}
pkg/feeds/feed.go
View file @
1de167ea
...
...
@@ -112,10 +112,16 @@ func NewUpdate(f *Feed, idx Index, timestamp int64, payload []byte, sig []byte)
return
nil
,
fmt
.
Errorf
(
"toChunk: %w"
,
err
)
}
ch
,
err
:=
soc
.
NewSignedChunk
(
id
,
cac
,
f
.
Owner
.
Bytes
(),
sig
)
ss
,
err
:=
soc
.
NewSigned
(
id
,
cac
,
f
.
Owner
.
Bytes
(),
sig
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"new signed soc: %w"
,
err
)
}
ch
,
err
:=
ss
.
Chunk
()
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"new chunk: %w"
,
err
)
}
if
!
soc
.
Valid
(
ch
)
{
return
nil
,
storage
.
ErrInvalidChunk
}
...
...
@@ -135,7 +141,6 @@ func Id(topic []byte, index Index) ([]byte, error) {
}
i
:=
&
id
{
topic
,
indexBytes
}
return
i
.
MarshalBinary
()
}
// Address calculates the soc address of a feed update
...
...
@@ -145,9 +150,5 @@ func (u *Update) Address() (swarm.Address, error) {
if
err
!=
nil
{
return
addr
,
err
}
owner
,
err
:=
soc
.
NewOwner
(
u
.
Owner
[
:
])
if
err
!=
nil
{
return
addr
,
err
}
return
soc
.
CreateAddress
(
i
,
owner
)
return
soc
.
CreateAddress
(
i
,
u
.
Owner
[
:
])
}
pkg/feeds/getter.go
View file @
1de167ea
...
...
@@ -55,7 +55,7 @@ func FromChunk(ch swarm.Chunk) (uint64, []byte, error) {
if
err
!=
nil
{
return
0
,
nil
,
err
}
cac
:=
s
.
Chunk
cac
:=
s
.
WrappedChunk
()
if
len
(
cac
.
Data
())
<
16
{
return
0
,
nil
,
fmt
.
Errorf
(
"feed update payload too short"
)
}
...
...
pkg/feeds/putter.go
View file @
1de167ea
...
...
@@ -48,7 +48,8 @@ func (u *Putter) Put(ctx context.Context, i Index, at int64, payload []byte) err
if
err
!=
nil
{
return
err
}
ch
,
err
:=
soc
.
NewChunk
(
id
,
cac
,
u
.
signer
)
s
:=
soc
.
New
(
id
,
cac
)
ch
,
err
:=
s
.
Sign
(
u
.
signer
)
if
err
!=
nil
{
return
err
}
...
...
pkg/soc/export_test.go
View file @
1de167ea
...
...
@@ -5,6 +5,22 @@
package
soc
var
(
ToSignDigest
=
toSignDigest
ErrInvalidAddress
=
errInvalidAddress
Hash
=
hash
RecoverAddress
=
recoverAddress
)
// Signature returns the SOC signature.
func
(
s
*
SOC
)
Signature
()
[]
byte
{
return
s
.
signature
}
// OwnerAddress returns the ethereum address of the SOC owner.
func
(
s
*
SOC
)
OwnerAddress
()
[]
byte
{
return
s
.
owner
}
// ID returns the SOC id.
func
(
s
*
SOC
)
ID
()
[]
byte
{
return
s
.
id
}
pkg/soc/soc.go
View file @
1de167ea
...
...
@@ -2,14 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package soc provides the single-owner chunk implemenation
// Package soc provides the single-owner chunk implemen
t
ation
// and validator.
package
soc
import
(
"bytes"
"errors"
"fmt"
"github.com/ethersphere/bee/pkg/cac"
"github.com/ethersphere/bee/pkg/crypto"
...
...
@@ -22,130 +21,118 @@ const (
minChunkSize
=
IdSize
+
SignatureSize
+
swarm
.
SpanSize
)
// Id is a soc identifier
type
Id
[]
byte
// Owner is a wrapper that enforces valid length address of soc owner.
type
Owner
struct
{
address
[]
byte
}
var
(
errInvalidAddress
=
errors
.
New
(
"soc: invalid address"
)
errWrongChunkSize
=
errors
.
New
(
"soc: chunk length is less than minimum"
)
)
// NewOwner creates a new Owner.
func
NewOwner
(
address
[]
byte
)
(
*
Owner
,
error
)
{
if
len
(
address
)
!=
crypto
.
AddressSize
{
return
nil
,
fmt
.
Errorf
(
"invalid address %x"
,
address
)
}
return
&
Owner
{
address
:
address
,
},
nil
}
// ID is a SOC identifier
type
ID
[]
byte
// Soc wraps a single soc.
type
Soc
struct
{
id
Id
// SOC wraps a content-addressed chunk.
type
SOC
struct
{
id
ID
owner
[]
byte
// owner is the address in bytes of SOC owner.
signature
[]
byte
signer
crypto
.
Signer
owner
*
Owner
Chunk
swarm
.
Chunk
chunk
swarm
.
Chunk
// wrapped chunk.
}
// NewChunk is a convenience function to create a single-owner chunk ready to be sent
// on the network.
func
NewChunk
(
id
Id
,
ch
swarm
.
Chunk
,
signer
crypto
.
Signer
)
(
swarm
.
Chunk
,
error
)
{
s
:=
New
(
id
,
ch
)
err
:=
s
.
AddSigner
(
signer
)
if
err
!=
nil
{
return
nil
,
err
// New creates a new SOC representation from arbitrary id and
// a content-addressed chunk.
func
New
(
id
ID
,
ch
swarm
.
Chunk
)
*
SOC
{
return
&
SOC
{
id
:
id
,
chunk
:
ch
,
}
return
s
.
ToChunk
()
}
// NewChunk is a convenience function to create a single-owner chunk ready to be sent
// on the network.
func
NewSignedChunk
(
id
Id
,
ch
swarm
.
Chunk
,
owner
,
sig
[]
byte
)
(
swarm
.
Chunk
,
error
)
{
// NewSigned creates a single-owner chunk based on already signed data.
func
NewSigned
(
id
ID
,
ch
swarm
.
Chunk
,
owner
,
sig
[]
byte
)
(
*
SOC
,
error
)
{
s
:=
New
(
id
,
ch
)
if
len
(
owner
)
!=
crypto
.
AddressSize
{
return
nil
,
errInvalidAddress
}
s
.
owner
=
owner
s
.
signature
=
sig
o
,
err
:=
NewOwner
(
owner
)
if
err
!=
nil
{
return
nil
,
err
return
s
,
nil
}
// address returns the SOC chunk address.
func
(
s
*
SOC
)
address
()
(
swarm
.
Address
,
error
)
{
if
len
(
s
.
owner
)
!=
crypto
.
AddressSize
{
return
swarm
.
ZeroAddress
,
errInvalidAddress
}
s
.
owner
=
o
return
CreateAddress
(
s
.
id
,
s
.
owner
)
}
// WrappedChunk returns the chunk wrapped by the SOC.
func
(
s
*
SOC
)
WrappedChunk
()
swarm
.
Chunk
{
return
s
.
chunk
}
// create chunk
socAddress
,
err
:=
s
.
Address
()
// Chunk returns the SOC chunk.
func
(
s
*
SOC
)
Chunk
()
(
swarm
.
Chunk
,
error
)
{
socAddress
,
err
:=
s
.
address
()
if
err
!=
nil
{
return
nil
,
err
}
return
swarm
.
NewChunk
(
socAddress
,
s
.
toBytes
()),
nil
}
// New creates a new Soc representation from arbitrary soc id and
// a content-addressed chunk.
//
// By default the span of the soc data is set to the length
// of the payload.
func
New
(
id
Id
,
ch
swarm
.
Chunk
)
*
Soc
{
return
&
Soc
{
id
:
id
,
Chunk
:
ch
,
}
}
// WithOwnerAddress provides the possibility of setting the ethereum
// address for the owner of an soc in the absence of a signer.
func
(
s
*
Soc
)
WithOwnerAddress
(
ownerAddress
*
Owner
)
*
Soc
{
s
.
owner
=
ownerAddress
return
s
// toBytes is a helper function to convert the SOC data to bytes.
func
(
s
*
SOC
)
toBytes
()
[]
byte
{
buf
:=
bytes
.
NewBuffer
(
nil
)
buf
.
Write
(
s
.
id
)
buf
.
Write
(
s
.
signature
)
buf
.
Write
(
s
.
chunk
.
Data
())
return
buf
.
Bytes
()
}
// AddSigner currently sets a single signer for the soc.
//
// This method will overwrite any value set with WithOwnerAddress with
// the address derived from the given signer.
func
(
s
*
Soc
)
AddSigner
(
signer
crypto
.
Signer
)
error
{
// Sign signs a SOC using the given signer.
// It returns a signed SOC chunk ready for submission to the network.
func
(
s
*
SOC
)
Sign
(
signer
crypto
.
Signer
)
(
swarm
.
Chunk
,
error
)
{
// create owner
publicKey
,
err
:=
signer
.
PublicKey
()
if
err
!=
nil
{
return
err
return
nil
,
err
}
ownerAddressBytes
,
err
:=
crypto
.
NewEthereumAddress
(
*
publicKey
)
if
err
!=
nil
{
return
err
return
nil
,
err
}
ownerAddress
,
err
:=
NewOwner
(
ownerAddressBytes
)
if
err
!=
nil
{
return
err
if
len
(
ownerAddressBytes
)
!=
crypto
.
AddressSize
{
return
nil
,
errInvalidAddress
}
s
.
signer
=
signer
s
.
owner
=
ownerAddress
return
nil
}
s
.
owner
=
ownerAddressBytes
// OwnerAddress returns the ethereum address of the signer of the Chunk.
func
(
s
*
Soc
)
OwnerAddress
()
[]
byte
{
return
s
.
owner
.
address
}
// generate the data to sign
toSignBytes
,
err
:=
hash
(
s
.
id
,
s
.
chunk
.
Address
()
.
Bytes
())
if
err
!=
nil
{
return
nil
,
err
}
// Address returns the soc Chunk address.
func
(
s
*
Soc
)
Address
()
(
swarm
.
Address
,
error
)
{
return
CreateAddress
(
s
.
id
,
s
.
owner
)
}
// sign the chunk
signature
,
err
:=
signer
.
Sign
(
toSignBytes
)
if
err
!=
nil
{
return
nil
,
err
}
s
.
signature
=
signature
func
(
s
*
Soc
)
Signature
()
[]
byte
{
return
s
.
signature
return
s
.
Chunk
()
}
// FromChunk recreates a
n Soc
representation from swarm.Chunk data.
func
FromChunk
(
sch
swarm
.
Chunk
)
(
*
S
oc
,
error
)
{
// FromChunk recreates a
SOC
representation from swarm.Chunk data.
func
FromChunk
(
sch
swarm
.
Chunk
)
(
*
S
OC
,
error
)
{
chunkData
:=
sch
.
Data
()
if
len
(
chunkData
)
<
minChunkSize
{
return
nil
,
err
ors
.
New
(
"less than minimum length"
)
return
nil
,
err
WrongChunkSize
}
// add all the data fields
s
:=
&
S
oc
{}
// add all the data fields
to the SOC
s
:=
&
S
OC
{}
cursor
:=
0
s
.
id
=
chunkData
[
cursor
:
cursor
+
IdSize
]
s
.
id
=
chunkData
[
cursor
:
IdSize
]
cursor
+=
IdSize
s
.
signature
=
chunkData
[
cursor
:
cursor
+
SignatureSize
]
...
...
@@ -156,94 +143,48 @@ func FromChunk(sch swarm.Chunk) (*Soc, error) {
return
nil
,
err
}
toSignBytes
,
err
:=
toSignDigest
(
s
.
id
,
ch
.
Address
()
.
Bytes
())
toSignBytes
,
err
:=
hash
(
s
.
id
,
ch
.
Address
()
.
Bytes
())
if
err
!=
nil
{
return
nil
,
err
}
// recover owner information
recovered
Ethereum
Address
,
err
:=
recoverAddress
(
s
.
signature
,
toSignBytes
)
recovered
Owner
Address
,
err
:=
recoverAddress
(
s
.
signature
,
toSignBytes
)
if
err
!=
nil
{
return
nil
,
err
}
owner
,
err
:=
NewOwner
(
recoveredEthereumAddress
)
if
err
!=
nil
{
return
nil
,
err
if
len
(
recoveredOwnerAddress
)
!=
crypto
.
AddressSize
{
return
nil
,
errInvalidAddress
}
s
.
owner
=
owner
s
.
C
hunk
=
ch
s
.
owner
=
recoveredOwnerAddress
s
.
c
hunk
=
ch
return
s
,
nil
}
// ToChunk generates a signed chunk payload ready for submission to the swarm network.
//
// The method will fail if no signer has been added.
func
(
s
*
Soc
)
ToChunk
()
(
swarm
.
Chunk
,
error
)
{
var
err
error
if
s
.
signer
==
nil
{
return
nil
,
errors
.
New
(
"signer missing"
)
}
// generate the data to sign
toSignBytes
,
err
:=
toSignDigest
(
s
.
id
,
s
.
Chunk
.
Address
()
.
Bytes
())
// CreateAddress creates a new SOC address from the id and
// the ethereum address of the owner.
func
CreateAddress
(
id
ID
,
owner
[]
byte
)
(
swarm
.
Address
,
error
)
{
sum
,
err
:=
hash
(
id
,
owner
)
if
err
!=
nil
{
return
nil
,
err
}
// sign the chunk
signature
,
err
:=
s
.
signer
.
Sign
(
toSignBytes
)
if
err
!=
nil
{
return
nil
,
err
}
s
.
signature
=
signature
// create chunk
socAddress
,
err
:=
s
.
Address
()
if
err
!=
nil
{
return
nil
,
err
return
swarm
.
ZeroAddress
,
err
}
return
swarm
.
NewChunk
(
socAddress
,
s
.
toBytes
()),
nil
}
func
(
s
*
Soc
)
toBytes
()
[]
byte
{
buf
:=
bytes
.
NewBuffer
(
nil
)
buf
.
Write
(
s
.
id
)
buf
.
Write
(
s
.
signature
)
buf
.
Write
(
s
.
Chunk
.
Data
())
return
buf
.
Bytes
()
return
swarm
.
NewAddress
(
sum
),
nil
}
//
toSignDigest creates a digest suitable for signing to represent the soc
.
func
toSignDigest
(
id
Id
,
sum
[]
byte
)
([]
byte
,
error
)
{
//
hash hashes the given values in order
.
func
hash
(
values
...
[]
byte
)
([]
byte
,
error
)
{
h
:=
swarm
.
NewHasher
()
_
,
err
:=
h
.
Write
(
id
)
for
_
,
v
:=
range
values
{
_
,
err
:=
h
.
Write
(
v
)
if
err
!=
nil
{
return
nil
,
err
}
_
,
err
=
h
.
Write
(
sum
)
if
err
!=
nil
{
return
nil
,
err
}
return
h
.
Sum
(
nil
),
nil
}
// CreateAddress creates a new soc address from the soc id and the ethereum address of the signer
func
CreateAddress
(
id
Id
,
owner
*
Owner
)
(
swarm
.
Address
,
error
)
{
h
:=
swarm
.
NewHasher
()
_
,
err
:=
h
.
Write
(
id
)
if
err
!=
nil
{
return
swarm
.
ZeroAddress
,
err
}
_
,
err
=
h
.
Write
(
owner
.
address
)
if
err
!=
nil
{
return
swarm
.
ZeroAddress
,
err
}
sum
:=
h
.
Sum
(
nil
)
return
swarm
.
NewAddress
(
sum
),
nil
}
// recoverOwner returns the ethereum address of the owner of an soc.
// recoverAddress returns the ethereum address of the owner of a SOC.
func
recoverAddress
(
signature
,
digest
[]
byte
)
([]
byte
,
error
)
{
recoveredPublicKey
,
err
:=
crypto
.
Recover
(
signature
,
digest
)
if
err
!=
nil
{
...
...
pkg/soc/soc_test.go
View file @
1de167ea
...
...
@@ -7,118 +7,315 @@ package soc_test
import
(
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/cac"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/soc"
"github.com/ethersphere/bee/pkg/swarm"
)
// TestToChunk verifies that the chunk create from the Soc object
// corresponds to the soc spec.
func
TestToChunk
(
t
*
testing
.
T
)
{
privKey
,
err
:=
crypto
.
GenerateSecp256k1Key
()
func
TestNew
(
t
*
testing
.
T
)
{
payload
:=
[]
byte
(
"foo"
)
ch
,
err
:=
cac
.
New
(
payload
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
signer
:=
crypto
.
NewDefaultSigner
(
privKey
)
id
:=
make
([]
byte
,
32
)
id
:=
make
([]
byte
,
soc
.
IdSize
)
s
:=
soc
.
New
(
id
,
ch
)
// check SOC fields
if
!
bytes
.
Equal
(
s
.
ID
(),
id
)
{
t
.
Fatalf
(
"id mismatch. got %x want %x"
,
s
.
ID
(),
id
)
}
chunkData
:=
s
.
WrappedChunk
()
.
Data
()
spanBytes
:=
make
([]
byte
,
swarm
.
SpanSize
)
binary
.
LittleEndian
.
PutUint64
(
spanBytes
,
uint64
(
len
(
payload
)))
if
!
bytes
.
Equal
(
chunkData
[
:
swarm
.
SpanSize
],
spanBytes
)
{
t
.
Fatalf
(
"span mismatch. got %x want %x"
,
chunkData
[
:
swarm
.
SpanSize
],
spanBytes
)
}
if
!
bytes
.
Equal
(
chunkData
[
swarm
.
SpanSize
:
],
payload
)
{
t
.
Fatalf
(
"payload mismatch. got %x want %x"
,
chunkData
[
swarm
.
SpanSize
:
],
payload
)
}
}
func
TestNewSigned
(
t
*
testing
.
T
)
{
owner
:=
common
.
HexToAddress
(
"8d3766440f0d7b949a5e32995d09619a7f86e632"
)
// signature of hash(id + chunk address of foo)
sig
,
err
:=
hex
.
DecodeString
(
"5acd384febc133b7b245e5ddc62d82d2cded9182d2716126cd8844509af65a053deb418208027f548e3e88343af6f84a8772fb3cebc0a1833a0ea7ec0c1348311b"
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
payload
:=
[]
byte
(
"foo"
)
ch
,
err
:=
cac
.
New
(
payload
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
sch
,
err
:=
soc
.
NewChunk
(
id
,
ch
,
signer
)
id
:=
make
([]
byte
,
soc
.
IdSize
)
s
,
err
:=
soc
.
NewSigned
(
id
,
ch
,
owner
.
Bytes
(),
sig
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
chunkData
:=
sch
.
Data
()
// verify that id, signature, payload is in place
// check signed SOC fields
if
!
bytes
.
Equal
(
s
.
ID
(),
id
)
{
t
.
Fatalf
(
"id mismatch. got %x want %x"
,
s
.
ID
(),
id
)
}
if
!
bytes
.
Equal
(
s
.
OwnerAddress
(),
owner
.
Bytes
())
{
t
.
Fatalf
(
"owner mismatch. got %x want %x"
,
s
.
OwnerAddress
(),
owner
.
Bytes
())
}
if
!
bytes
.
Equal
(
s
.
Signature
(),
sig
)
{
t
.
Fatalf
(
"signature mismatch. got %x want %x"
,
s
.
Signature
(),
sig
)
}
chunkData
:=
s
.
WrappedChunk
()
.
Data
()
spanBytes
:=
make
([]
byte
,
swarm
.
SpanSize
)
binary
.
LittleEndian
.
PutUint64
(
spanBytes
,
uint64
(
len
(
payload
)))
if
!
bytes
.
Equal
(
chunkData
[
:
swarm
.
SpanSize
],
spanBytes
)
{
t
.
Fatalf
(
"span mismatch. got %x want %x"
,
chunkData
[
:
swarm
.
SpanSize
],
spanBytes
)
}
if
!
bytes
.
Equal
(
chunkData
[
swarm
.
SpanSize
:
],
payload
)
{
t
.
Fatalf
(
"payload mismatch. got %x want %x"
,
chunkData
[
swarm
.
SpanSize
:
],
payload
)
}
}
// TestChunk verifies that the chunk created from the SOC object
// corresponds to the SOC spec.
func
TestChunk
(
t
*
testing
.
T
)
{
owner
:=
common
.
HexToAddress
(
"8d3766440f0d7b949a5e32995d09619a7f86e632"
)
sig
,
err
:=
hex
.
DecodeString
(
"5acd384febc133b7b245e5ddc62d82d2cded9182d2716126cd8844509af65a053deb418208027f548e3e88343af6f84a8772fb3cebc0a1833a0ea7ec0c1348311b"
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
payload
:=
[]
byte
(
"foo"
)
ch
,
err
:=
cac
.
New
(
payload
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
id
:=
make
([]
byte
,
soc
.
IdSize
)
// creates a new signed SOC
s
,
err
:=
soc
.
NewSigned
(
id
,
ch
,
owner
.
Bytes
(),
sig
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
sum
,
err
:=
soc
.
Hash
(
id
,
owner
.
Bytes
())
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
expectedSOCAddress
:=
swarm
.
NewAddress
(
sum
)
// creates SOC chunk
sch
,
err
:=
s
.
Chunk
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
!
bytes
.
Equal
(
sch
.
Address
()
.
Bytes
(),
expectedSOCAddress
.
Bytes
())
{
t
.
Fatalf
(
"soc address mismatch. got %x want %x"
,
sch
.
Address
()
.
Bytes
(),
expectedSOCAddress
.
Bytes
())
}
chunkData
:=
sch
.
Data
()
// verifies that id, signature, payload is in place in the SOC chunk
cursor
:=
0
if
!
bytes
.
Equal
(
chunkData
[
cursor
:
cursor
+
soc
.
IdSize
],
id
)
{
t
.
Fatal
(
"id mismatch"
)
if
!
bytes
.
Equal
(
chunkData
[
cursor
:
soc
.
IdSize
],
id
)
{
t
.
Fatal
f
(
"id mismatch. got %x want %x"
,
chunkData
[
cursor
:
soc
.
IdSize
],
id
)
}
cursor
+=
soc
.
IdSize
signature
:=
chunkData
[
cursor
:
cursor
+
soc
.
SignatureSize
]
if
!
bytes
.
Equal
(
signature
,
sig
)
{
t
.
Fatalf
(
"signature mismatch. got %x want %x"
,
signature
,
sig
)
}
cursor
+=
soc
.
SignatureSize
spanBytes
:=
make
([]
byte
,
8
)
spanBytes
:=
make
([]
byte
,
swarm
.
SpanSize
)
binary
.
LittleEndian
.
PutUint64
(
spanBytes
,
uint64
(
len
(
payload
)))
if
!
bytes
.
Equal
(
chunkData
[
cursor
:
cursor
+
swarm
.
SpanSize
],
spanBytes
)
{
t
.
Fatal
(
"span mismatch"
)
t
.
Fatal
f
(
"span mismatch. got %x want %x"
,
chunkData
[
cursor
:
cursor
+
swarm
.
SpanSize
],
spanBytes
)
}
cursor
+=
swarm
.
SpanSize
if
!
bytes
.
Equal
(
chunkData
[
cursor
:
],
payload
)
{
t
.
Fatal
(
"payload mismatch"
)
t
.
Fatalf
(
"payload mismatch. got %x want %x"
,
chunkData
[
cursor
:
],
payload
)
}
}
func
TestChunkErrorWithoutOwner
(
t
*
testing
.
T
)
{
payload
:=
[]
byte
(
"foo"
)
ch
,
err
:=
cac
.
New
(
payload
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
id
:=
make
([]
byte
,
soc
.
IdSize
)
// creates a new soc
s
:=
soc
.
New
(
id
,
ch
)
_
,
err
=
s
.
Chunk
()
if
!
errors
.
Is
(
err
,
soc
.
ErrInvalidAddress
)
{
t
.
Fatalf
(
"expect error. got `%v` want `%v`"
,
err
,
soc
.
ErrInvalidAddress
)
}
}
// TestSign tests whether a soc is correctly signed.
func
TestSign
(
t
*
testing
.
T
)
{
privKey
,
err
:=
crypto
.
GenerateSecp256k1Key
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
signer
:=
crypto
.
NewDefaultSigner
(
privKey
)
// get the public key of the signer that was used
payload
:=
[]
byte
(
"foo"
)
ch
,
err
:=
cac
.
New
(
payload
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
id
:=
make
([]
byte
,
soc
.
IdSize
)
// creates the soc
s
:=
soc
.
New
(
id
,
ch
)
// signs the chunk
sch
,
err
:=
s
.
Sign
(
signer
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
chunkData
:=
sch
.
Data
()
// get signature in the chunk
cursor
:=
soc
.
IdSize
signature
:=
chunkData
[
cursor
:
cursor
+
soc
.
SignatureSize
]
// get the public key of the signer
publicKey
,
err
:=
signer
.
PublicKey
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
ethereumAddress
,
err
:=
crypto
.
NewEthereumAddress
(
*
publicKey
)
owner
,
err
:=
crypto
.
NewEthereumAddress
(
*
publicKey
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
toSignBytes
,
err
:=
soc
.
ToSignDigest
(
id
,
ch
.
Address
()
.
Bytes
())
toSignBytes
,
err
:=
soc
.
Hash
(
id
,
ch
.
Address
()
.
Bytes
())
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
// verif
y owner match
recovered
EthereumAddress
,
err
:=
soc
.
RecoverAddress
(
signature
,
toSignBytes
)
// verif
ies if the owner matches
recovered
Owner
,
err
:=
soc
.
RecoverAddress
(
signature
,
toSignBytes
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
!
bytes
.
Equal
(
recoveredEthereumAddress
,
ethereumAddress
)
{
t
.
Fatalf
(
"address mismatch %x %x"
,
recoveredEthereumAddress
,
ethereumAddress
)
if
!
bytes
.
Equal
(
recoveredOwner
,
owner
)
{
t
.
Fatalf
(
"owner address mismatch. got %x want %x"
,
recoveredOwner
,
owner
)
}
}
// TestFromChunk verifies that valid chunk data deserializes to
// a fully populated
Chunk
object.
// a fully populated
soc
object.
func
TestFromChunk
(
t
*
testing
.
T
)
{
privKey
,
err
:=
crypto
.
GenerateSecp256k1Key
()
socAddress
:=
swarm
.
MustParseHexAddress
(
"9d453ebb73b2fedaaf44ceddcf7a0aa37f3e3d6453fea5841c31f0ea6d61dc85"
)
// signed soc chunk of:
// id: 0
// wrapped chunk of: `foo`
// owner: 0x8d3766440f0d7b949a5e32995d09619a7f86e632
sch
:=
swarm
.
NewChunk
(
socAddress
,
[]
byte
{
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
90
,
205
,
56
,
79
,
235
,
193
,
51
,
183
,
178
,
69
,
229
,
221
,
198
,
45
,
130
,
210
,
205
,
237
,
145
,
130
,
210
,
113
,
97
,
38
,
205
,
136
,
68
,
80
,
154
,
246
,
90
,
5
,
61
,
235
,
65
,
130
,
8
,
2
,
127
,
84
,
142
,
62
,
136
,
52
,
58
,
246
,
248
,
74
,
135
,
114
,
251
,
60
,
235
,
192
,
161
,
131
,
58
,
14
,
167
,
236
,
12
,
19
,
72
,
49
,
27
,
3
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
102
,
111
,
111
})
cursor
:=
soc
.
IdSize
+
soc
.
SignatureSize
data
:=
sch
.
Data
()
id
:=
data
[
:
soc
.
IdSize
]
sig
:=
data
[
soc
.
IdSize
:
cursor
]
chunkData
:=
data
[
cursor
:
]
chunkAddress
:=
swarm
.
MustParseHexAddress
(
"2387e8e7d8a48c2a9339c97c1dc3461a9a7aa07e994c5cb8b38fd7c1b3e6ea48"
)
ch
:=
swarm
.
NewChunk
(
chunkAddress
,
chunkData
)
signedDigest
,
err
:=
soc
.
Hash
(
id
,
ch
.
Address
()
.
Bytes
())
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
signer
:=
crypto
.
NewDefaultSigner
(
privKey
)
id
:=
make
([]
byte
,
32
)
ownerAddress
,
err
:=
soc
.
RecoverAddress
(
sig
,
signedDigest
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
payload
:=
[]
byte
(
"foo"
)
ch
,
err
:=
cac
.
New
(
payload
)
// attempt to recover soc from signed chunk
recoveredSOC
,
err
:=
soc
.
FromChunk
(
sch
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
sch
,
err
:=
soc
.
NewChunk
(
id
,
ch
,
signer
)
// owner matching means the address was successfully recovered from
// payload and signature
if
!
bytes
.
Equal
(
recoveredSOC
.
OwnerAddress
(),
ownerAddress
)
{
t
.
Fatalf
(
"owner address mismatch. got %x want %x"
,
recoveredSOC
.
OwnerAddress
(),
ownerAddress
)
}
if
!
bytes
.
Equal
(
recoveredSOC
.
ID
(),
id
)
{
t
.
Fatalf
(
"id mismatch. got %x want %x"
,
recoveredSOC
.
ID
(),
id
)
}
if
!
bytes
.
Equal
(
recoveredSOC
.
Signature
(),
sig
)
{
t
.
Fatalf
(
"signature mismatch. got %x want %x"
,
recoveredSOC
.
Signature
(),
sig
)
}
if
!
ch
.
Equal
(
recoveredSOC
.
WrappedChunk
())
{
t
.
Fatalf
(
"wrapped chunk mismatch. got %s want %s"
,
recoveredSOC
.
WrappedChunk
()
.
Address
(),
ch
.
Address
())
}
}
func
TestCreateAddress
(
t
*
testing
.
T
)
{
id
:=
make
([]
byte
,
soc
.
IdSize
)
owner
:=
common
.
HexToAddress
(
"8d3766440f0d7b949a5e32995d09619a7f86e632"
)
socAddress
:=
swarm
.
MustParseHexAddress
(
"9d453ebb73b2fedaaf44ceddcf7a0aa37f3e3d6453fea5841c31f0ea6d61dc85"
)
addr
,
err
:=
soc
.
CreateAddress
(
id
,
owner
.
Bytes
())
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
!
addr
.
Equal
(
socAddress
)
{
t
.
Fatalf
(
"soc address mismatch. got %s want %s"
,
addr
,
socAddress
)
}
}
u2
,
err
:=
soc
.
FromChunk
(
sch
)
func
TestRecoverAddress
(
t
*
testing
.
T
)
{
owner
:=
common
.
HexToAddress
(
"8d3766440f0d7b949a5e32995d09619a7f86e632"
)
id
:=
make
([]
byte
,
soc
.
IdSize
)
chunkAddress
:=
swarm
.
MustParseHexAddress
(
"2387e8e7d8a48c2a9339c97c1dc3461a9a7aa07e994c5cb8b38fd7c1b3e6ea48"
)
signedDigest
,
err
:=
soc
.
Hash
(
id
,
chunkAddress
.
Bytes
())
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
// owner matching means the address was successfully recovered from
// payload and signature
publicKey
,
err
:=
signer
.
PublicKey
()
sig
,
err
:=
hex
.
DecodeString
(
"5acd384febc133b7b245e5ddc62d82d2cded9182d2716126cd8844509af65a053deb418208027f548e3e88343af6f84a8772fb3cebc0a1833a0ea7ec0c1348311b"
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
ownerEthereumAddress
,
err
:=
crypto
.
NewEthereumAddress
(
*
publicKey
)
// attempt to recover address from signature
addr
,
err
:=
soc
.
RecoverAddress
(
sig
,
signedDigest
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
!
bytes
.
Equal
(
ownerEthereumAddress
,
u2
.
OwnerAddres
s
())
{
t
.
Fatalf
(
"owner address mismatch
%x %x"
,
ownerEthereumAddress
,
u2
.
OwnerAddres
s
())
if
!
bytes
.
Equal
(
addr
,
owner
.
Byte
s
())
{
t
.
Fatalf
(
"owner address mismatch
. got %x want %x"
,
addr
,
owner
.
Byte
s
())
}
}
pkg/soc/testing/soc.go
0 → 100644
View file @
1de167ea
// Copyright 2021 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
testing
import
(
"testing"
"github.com/ethersphere/bee/pkg/cac"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/soc"
"github.com/ethersphere/bee/pkg/swarm"
)
// MockSOC defines a mocked SOC with exported fields for easy testing.
type
MockSOC
struct
{
ID
soc
.
ID
Owner
[]
byte
Signature
[]
byte
WrappedChunk
swarm
.
Chunk
}
// Address returns the SOC address of the mocked SOC.
func
(
ms
MockSOC
)
Address
()
swarm
.
Address
{
addr
,
_
:=
soc
.
CreateAddress
(
ms
.
ID
,
ms
.
Owner
)
return
addr
}
// Chunk returns the SOC chunk of the mocked SOC.
func
(
ms
MockSOC
)
Chunk
()
swarm
.
Chunk
{
return
swarm
.
NewChunk
(
ms
.
Address
(),
append
(
ms
.
ID
,
append
(
ms
.
Signature
,
ms
.
WrappedChunk
.
Data
()
...
)
...
))
}
// GenerateMockSOC generates a valid mocked SOC from given data.
func
GenerateMockSOC
(
t
*
testing
.
T
,
data
[]
byte
)
*
MockSOC
{
t
.
Helper
()
privKey
,
err
:=
crypto
.
GenerateSecp256k1Key
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
signer
:=
crypto
.
NewDefaultSigner
(
privKey
)
owner
,
err
:=
signer
.
EthereumAddress
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
ch
,
err
:=
cac
.
New
(
data
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
id
:=
make
([]
byte
,
soc
.
IdSize
)
hasher
:=
swarm
.
NewHasher
()
_
,
err
=
hasher
.
Write
(
append
(
id
,
ch
.
Address
()
.
Bytes
()
...
))
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
signature
,
err
:=
signer
.
Sign
(
hasher
.
Sum
(
nil
))
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
return
&
MockSOC
{
ID
:
id
,
Owner
:
owner
.
Bytes
(),
Signature
:
signature
,
WrappedChunk
:
ch
,
}
}
pkg/soc/validator.go
View file @
1de167ea
...
...
@@ -15,7 +15,7 @@ func Valid(ch swarm.Chunk) bool {
return
false
}
address
,
err
:=
s
.
A
ddress
()
address
,
err
:=
s
.
a
ddress
()
if
err
!=
nil
{
return
false
}
...
...
pkg/soc/validator_test.go
View file @
1de167ea
...
...
@@ -5,57 +5,110 @@
package
soc_test
import
(
"
encoding/binary
"
"
strings
"
"testing"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/soc"
"github.com/ethersphere/bee/pkg/swarm"
)
// TestValidator verifies that the validator can detect both
// valid soc chunks, as well as chunks with invalid data and invalid
// address.
func
TestValidator
(
t
*
testing
.
T
)
{
id
:=
make
([]
byte
,
soc
.
IdSize
)
privKey
,
err
:=
crypto
.
GenerateSecp256k1Key
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
signer
:=
crypto
.
NewDefaultSigner
(
privKey
)
bmtHashOfFoo
:=
"2387e8e7d8a48c2a9339c97c1dc3461a9a7aa07e994c5cb8b38fd7c1b3e6ea48"
address
:=
swarm
.
MustParseHexAddress
(
bmtHashOfFoo
)
foo
:=
"foo"
fooLength
:=
len
(
foo
)
fooBytes
:=
make
([]
byte
,
8
+
fooLength
)
binary
.
LittleEndian
.
PutUint64
(
fooBytes
,
uint64
(
fooLength
))
copy
(
fooBytes
[
8
:
],
foo
)
ch
:=
swarm
.
NewChunk
(
address
,
fooBytes
)
sch
,
err
:=
soc
.
NewChunk
(
id
,
ch
,
signer
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
// TestValid verifies that the validator can detect
// valid soc chunks.
func
TestValid
(
t
*
testing
.
T
)
{
socAddress
:=
swarm
.
MustParseHexAddress
(
"9d453ebb73b2fedaaf44ceddcf7a0aa37f3e3d6453fea5841c31f0ea6d61dc85"
)
// signed soc chunk of:
// id: 0
// wrapped chunk of: `foo`
// owner: 0x8d3766440f0d7b949a5e32995d09619a7f86e632
sch
:=
swarm
.
NewChunk
(
socAddress
,
[]
byte
{
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
90
,
205
,
56
,
79
,
235
,
193
,
51
,
183
,
178
,
69
,
229
,
221
,
198
,
45
,
130
,
210
,
205
,
237
,
145
,
130
,
210
,
113
,
97
,
38
,
205
,
136
,
68
,
80
,
154
,
246
,
90
,
5
,
61
,
235
,
65
,
130
,
8
,
2
,
127
,
84
,
142
,
62
,
136
,
52
,
58
,
246
,
248
,
74
,
135
,
114
,
251
,
60
,
235
,
192
,
161
,
131
,
58
,
14
,
167
,
236
,
12
,
19
,
72
,
49
,
27
,
3
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
102
,
111
,
111
})
// check valid chunk
if
!
soc
.
Valid
(
sch
)
{
t
.
Fatal
(
"valid chunk evaluates to invalid"
)
}
}
// check invalid data
sch
.
Data
()[
0
]
=
0x01
if
soc
.
Valid
(
sch
)
{
t
.
Fatal
(
"chunk with invalid data evaluates to valid"
)
}
// TestInvalid verifies that the validator can detect chunks
// with invalid data and invalid address.
func
TestInvalid
(
t
*
testing
.
T
)
{
socAddress
:=
swarm
.
MustParseHexAddress
(
"9d453ebb73b2fedaaf44ceddcf7a0aa37f3e3d6453fea5841c31f0ea6d61dc85"
)
// signed soc chunk of:
// id: 0
// wrapped chunk of: `foo`
// owner: 0x8d3766440f0d7b949a5e32995d09619a7f86e632
sch
:=
swarm
.
NewChunk
(
socAddress
,
[]
byte
{
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
90
,
205
,
56
,
79
,
235
,
193
,
51
,
183
,
178
,
69
,
229
,
221
,
198
,
45
,
130
,
210
,
205
,
237
,
145
,
130
,
210
,
113
,
97
,
38
,
205
,
136
,
68
,
80
,
154
,
246
,
90
,
5
,
61
,
235
,
65
,
130
,
8
,
2
,
127
,
84
,
142
,
62
,
136
,
52
,
58
,
246
,
248
,
74
,
135
,
114
,
251
,
60
,
235
,
192
,
161
,
131
,
58
,
14
,
167
,
236
,
12
,
19
,
72
,
49
,
27
,
3
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
102
,
111
,
111
})
// check invalid address
sch
.
Data
()[
0
]
=
0x00
for
_
,
c
:=
range
[]
struct
{
name
string
chunk
func
()
swarm
.
Chunk
}{
{
name
:
"wrong soc address"
,
chunk
:
func
()
swarm
.
Chunk
{
wrongAddressBytes
:=
sch
.
Address
()
.
Bytes
()
wrongAddressBytes
[
0
]
=
255
-
wrongAddressBytes
[
0
]
wrongAddress
:=
swarm
.
NewAddress
(
wrongAddressBytes
)
sch
=
swarm
.
NewChunk
(
wrongAddress
,
sch
.
Data
())
if
soc
.
Valid
(
sch
)
{
t
.
Fatal
(
"chunk with invalid address evaluates to valid"
)
return
swarm
.
NewChunk
(
wrongAddress
,
sch
.
Data
())
},
},
{
name
:
"invalid data"
,
chunk
:
func
()
swarm
.
Chunk
{
data
:=
make
([]
byte
,
len
(
sch
.
Data
()))
copy
(
data
,
sch
.
Data
())
cursor
:=
soc
.
IdSize
+
soc
.
SignatureSize
chunkData
:=
data
[
cursor
:
]
chunkData
[
0
]
=
0x01
return
swarm
.
NewChunk
(
socAddress
,
data
)
},
},
{
name
:
"invalid id"
,
chunk
:
func
()
swarm
.
Chunk
{
data
:=
make
([]
byte
,
len
(
sch
.
Data
()))
copy
(
data
,
sch
.
Data
())
id
:=
data
[
:
soc
.
IdSize
]
id
[
0
]
=
0x01
return
swarm
.
NewChunk
(
socAddress
,
data
)
},
},
{
name
:
"invalid signature"
,
chunk
:
func
()
swarm
.
Chunk
{
data
:=
make
([]
byte
,
len
(
sch
.
Data
()))
copy
(
data
,
sch
.
Data
())
// modify signature
cursor
:=
soc
.
IdSize
+
soc
.
SignatureSize
sig
:=
data
[
soc
.
IdSize
:
cursor
]
sig
[
0
]
=
0x01
return
swarm
.
NewChunk
(
socAddress
,
data
)
},
},
{
name
:
"nil data"
,
chunk
:
func
()
swarm
.
Chunk
{
return
swarm
.
NewChunk
(
socAddress
,
nil
)
},
},
{
name
:
"small data"
,
chunk
:
func
()
swarm
.
Chunk
{
return
swarm
.
NewChunk
(
socAddress
,
[]
byte
(
"small"
))
},
},
{
name
:
"large data"
,
chunk
:
func
()
swarm
.
Chunk
{
return
swarm
.
NewChunk
(
socAddress
,
[]
byte
(
strings
.
Repeat
(
"a"
,
swarm
.
ChunkSize
+
swarm
.
SpanSize
+
1
)))
},
},
}
{
t
.
Run
(
c
.
name
,
func
(
t
*
testing
.
T
)
{
if
soc
.
Valid
(
c
.
chunk
())
{
t
.
Fatal
(
"chunk with invalid data evaluates to valid"
)
}
})
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment