Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
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
exchain
nebula
Commits
19379a14
Unverified
Commit
19379a14
authored
Apr 14, 2023
by
protolambda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
op-program: pre-image oracle communication
parent
ceafc32e
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
304 additions
and
0 deletions
+304
-0
hints.go
op-program/preimage/hints.go
+65
-0
hints_test.go
op-program/preimage/hints_test.go
+74
-0
oracle.go
op-program/preimage/oracle.go
+80
-0
oracle_test.go
op-program/preimage/oracle_test.go
+85
-0
No files found.
op-program/preimage/hints.go
0 → 100644
View file @
19379a14
package
preimage
import
(
"encoding/binary"
"fmt"
"io"
)
// HintWriter writes hints to an io.Writer (e.g. a special file descriptor, or a debug log),
// for a pre-image oracle service to prepare specific pre-images.
type
HintWriter
struct
{
w
io
.
Writer
}
var
_
Hinter
=
(
*
HintWriter
)(
nil
)
func
NewHintWriter
(
w
io
.
Writer
)
*
HintWriter
{
return
&
HintWriter
{
w
:
w
}
}
func
(
hw
*
HintWriter
)
Hint
(
v
Hint
)
{
hint
:=
v
.
Hint
()
hintBytes
:=
make
([]
byte
,
4
,
4
+
len
(
hint
)
+
1
)
binary
.
BigEndian
.
PutUint32
(
hintBytes
[
:
4
],
uint32
(
len
(
hint
)))
hintBytes
=
append
(
hintBytes
,
[]
byte
(
hint
)
...
)
hintBytes
=
append
(
hintBytes
,
0
)
// to block writing on
_
,
err
:=
hw
.
w
.
Write
(
hintBytes
)
if
err
!=
nil
{
panic
(
fmt
.
Errorf
(
"failed to write pre-image hint: %w"
,
err
))
}
}
// HintReader reads the hints of HintWriter and passes them to a router for preparation of the requested pre-images.
// Onchain the written hints are no-op.
type
HintReader
struct
{
r
io
.
Reader
}
func
NewHintReader
(
r
io
.
Reader
)
*
HintReader
{
return
&
HintReader
{
r
:
r
}
}
func
(
hr
*
HintReader
)
NextHint
(
router
func
(
hint
string
)
error
)
error
{
var
lengthPrefix
[
4
]
byte
if
_
,
err
:=
io
.
ReadFull
(
hr
.
r
,
lengthPrefix
[
:
]);
err
!=
nil
{
if
err
==
io
.
EOF
{
return
io
.
EOF
}
return
fmt
.
Errorf
(
"failed to read hint length prefix: %w"
,
err
)
}
length
:=
binary
.
BigEndian
.
Uint32
(
lengthPrefix
[
:
])
payload
:=
make
([]
byte
,
length
)
if
length
>
0
{
if
_
,
err
:=
io
.
ReadFull
(
hr
.
r
,
payload
);
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to read hint payload (length %d): %w"
,
length
,
err
)
}
}
if
err
:=
router
(
string
(
payload
));
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to handle hint: %w"
,
err
)
}
if
_
,
err
:=
hr
.
r
.
Read
([]
byte
{
0
});
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to read trailing no-op byte to unblock hint writer: %w"
,
err
)
}
return
nil
}
op-program/preimage/hints_test.go
0 → 100644
View file @
19379a14
package
preimage
import
(
"bytes"
"crypto/rand"
"io"
"testing"
"github.com/stretchr/testify/require"
)
type
rawHint
string
func
(
rh
rawHint
)
Hint
()
string
{
return
string
(
rh
)
}
func
TestHints
(
t
*
testing
.
T
)
{
// Note: pretty much every string is valid communication:
// length, payload, 0. Worst case you run out of data, or allocate too much.
testHint
:=
func
(
hints
...
string
)
{
var
buf
bytes
.
Buffer
hw
:=
NewHintWriter
(
&
buf
)
for
_
,
h
:=
range
hints
{
hw
.
Hint
(
rawHint
(
h
))
}
hr
:=
NewHintReader
(
&
buf
)
var
got
[]
string
for
i
:=
0
;
i
<
100
;
i
++
{
// sanity limit
err
:=
hr
.
NextHint
(
func
(
hint
string
)
error
{
got
=
append
(
got
,
hint
)
return
nil
})
if
err
==
io
.
EOF
{
break
}
require
.
NoError
(
t
,
err
)
}
require
.
Equal
(
t
,
len
(
hints
),
len
(
got
),
"got all hints"
)
for
i
,
h
:=
range
hints
{
require
.
Equal
(
t
,
h
,
got
[
i
],
"hints match"
)
}
}
t
.
Run
(
"empty hint"
,
func
(
t
*
testing
.
T
)
{
testHint
(
""
)
})
t
.
Run
(
"hello world"
,
func
(
t
*
testing
.
T
)
{
testHint
(
"hello world"
)
})
t
.
Run
(
"zero byte"
,
func
(
t
*
testing
.
T
)
{
testHint
(
string
([]
byte
{
0
}))
})
t
.
Run
(
"many zeroes"
,
func
(
t
*
testing
.
T
)
{
testHint
(
string
(
make
([]
byte
,
1000
)))
})
t
.
Run
(
"random data"
,
func
(
t
*
testing
.
T
)
{
dat
:=
make
([]
byte
,
1000
)
_
,
_
=
rand
.
Read
(
dat
[
:
])
testHint
(
string
(
dat
))
})
t
.
Run
(
"multiple hints"
,
func
(
t
*
testing
.
T
)
{
testHint
(
"give me header a"
,
"also header b"
,
"foo bar"
)
})
t
.
Run
(
"unexpected EOF"
,
func
(
t
*
testing
.
T
)
{
var
buf
bytes
.
Buffer
hw
:=
NewHintWriter
(
&
buf
)
hw
.
Hint
(
rawHint
(
"hello"
))
_
,
_
=
buf
.
Read
(
make
([]
byte
,
1
))
// read one byte so it falls short, see if it's detected
hr
:=
NewHintReader
(
&
buf
)
err
:=
hr
.
NextHint
(
func
(
hint
string
)
error
{
return
nil
})
require
.
ErrorIs
(
t
,
err
,
io
.
ErrUnexpectedEOF
)
})
}
op-program/preimage/oracle.go
0 → 100644
View file @
19379a14
package
preimage
import
(
"encoding/binary"
"fmt"
"io"
"github.com/ethereum/go-ethereum/common"
)
// OracleClient implements the Oracle by writing the pre-image key to the given stream,
// and reading back a length-prefixed value.
type
OracleClient
struct
{
rw
io
.
ReadWriter
}
func
NewOracleClient
(
rw
io
.
ReadWriter
)
*
OracleClient
{
return
&
OracleClient
{
rw
:
rw
}
}
var
_
Oracle
=
(
*
OracleClient
)(
nil
)
func
(
o
*
OracleClient
)
Get
(
key
Key
)
[]
byte
{
h
:=
key
.
PreimageKey
()
if
_
,
err
:=
o
.
rw
.
Write
(
h
[
:
]);
err
!=
nil
{
panic
(
fmt
.
Errorf
(
"failed to write key %s (%T) to pre-image oracle: %w"
,
key
,
key
,
err
))
}
var
lengthPrefix
[
8
]
byte
if
_
,
err
:=
io
.
ReadFull
(
o
.
rw
,
lengthPrefix
[
:
]);
err
!=
nil
{
panic
(
fmt
.
Errorf
(
"failed to read pre-image length of key %s (%T) from pre-image oracle: %w"
,
key
,
key
,
err
))
}
length
:=
binary
.
BigEndian
.
Uint64
(
lengthPrefix
[
:
])
if
length
==
0
{
// don't read empty payloads
return
nil
}
payload
:=
make
([]
byte
,
length
)
if
_
,
err
:=
io
.
ReadFull
(
o
.
rw
,
payload
);
err
!=
nil
{
panic
(
fmt
.
Errorf
(
"failed to read pre-image payload (length %d) of key %s (%T) from pre-image oracle: %w"
,
length
,
key
,
key
,
err
))
}
return
payload
}
// OracleServer serves the pre-image requests of the OracleClient, implementing the same protocol as the onchain VM.
type
OracleServer
struct
{
rw
io
.
ReadWriter
}
func
NewOracleServer
(
rw
io
.
ReadWriter
)
*
OracleServer
{
return
&
OracleServer
{
rw
:
rw
}
}
func
(
o
*
OracleServer
)
NextPreimageRequest
(
getPreimage
func
(
key
common
.
Hash
)
([]
byte
,
error
))
error
{
var
key
common
.
Hash
if
_
,
err
:=
io
.
ReadFull
(
o
.
rw
,
key
[
:
]);
err
!=
nil
{
if
err
==
io
.
EOF
{
return
io
.
EOF
}
return
fmt
.
Errorf
(
"failed to read requested pre-image key: %w"
,
err
)
}
value
,
err
:=
getPreimage
(
key
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to serve pre-image %s request: %w"
,
key
,
err
)
}
var
lengthPrefix
[
8
]
byte
binary
.
BigEndian
.
PutUint64
(
lengthPrefix
[
:
],
uint64
(
len
(
value
)))
if
_
,
err
:=
o
.
rw
.
Write
(
lengthPrefix
[
:
]);
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to write length-prefix %x: %w"
,
lengthPrefix
,
err
)
}
if
len
(
value
)
==
0
{
return
nil
}
if
_
,
err
:=
o
.
rw
.
Write
(
value
);
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to write pre-image value (%d long): %w"
,
len
(
value
),
err
)
}
return
nil
}
op-program/preimage/oracle_test.go
0 → 100644
View file @
19379a14
package
preimage
import
(
"bytes"
"crypto/rand"
"fmt"
"io"
"sync"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
)
type
readWritePair
struct
{
io
.
Reader
io
.
Writer
}
func
bidirectionalPipe
()
(
a
,
b
io
.
ReadWriter
)
{
ar
,
bw
:=
io
.
Pipe
()
br
,
aw
:=
io
.
Pipe
()
return
readWritePair
{
Reader
:
ar
,
Writer
:
aw
},
readWritePair
{
Reader
:
br
,
Writer
:
bw
}
}
func
TestOracle
(
t
*
testing
.
T
)
{
testPreimage
:=
func
(
preimages
...
[]
byte
)
{
a
,
b
:=
bidirectionalPipe
()
cl
:=
NewOracleClient
(
a
)
srv
:=
NewOracleServer
(
b
)
preimageByHash
:=
make
(
map
[
common
.
Hash
][]
byte
)
for
_
,
p
:=
range
preimages
{
k
:=
Keccak256Key
(
crypto
.
Keccak256Hash
(
p
))
preimageByHash
[
k
.
PreimageKey
()]
=
p
}
for
_
,
p
:=
range
preimages
{
k
:=
Keccak256Key
(
crypto
.
Keccak256Hash
(
p
))
var
wg
sync
.
WaitGroup
wg
.
Add
(
2
)
go
func
(
k
Key
,
p
[]
byte
)
{
result
:=
cl
.
Get
(
k
)
wg
.
Done
()
expected
:=
preimageByHash
[
k
.
PreimageKey
()]
require
.
True
(
t
,
bytes
.
Equal
(
expected
,
result
),
"need correct preimage %x, got %x"
,
expected
,
result
)
}(
k
,
p
)
go
func
()
{
err
:=
srv
.
NextPreimageRequest
(
func
(
key
common
.
Hash
)
([]
byte
,
error
)
{
dat
,
ok
:=
preimageByHash
[
key
]
if
!
ok
{
return
nil
,
fmt
.
Errorf
(
"cannot find %s"
,
key
)
}
return
dat
,
nil
})
wg
.
Done
()
require
.
NoError
(
t
,
err
)
}()
wg
.
Wait
()
}
}
t
.
Run
(
"empty preimage"
,
func
(
t
*
testing
.
T
)
{
testPreimage
([]
byte
{})
})
t
.
Run
(
"nil preimage"
,
func
(
t
*
testing
.
T
)
{
testPreimage
(
nil
)
})
t
.
Run
(
"zero"
,
func
(
t
*
testing
.
T
)
{
testPreimage
([]
byte
{
0
})
})
t
.
Run
(
"multiple"
,
func
(
t
*
testing
.
T
)
{
testPreimage
([]
byte
(
"tx from alice"
),
[]
byte
{
0x13
,
0x37
},
[]
byte
(
"tx from bob"
))
})
t
.
Run
(
"zeroes"
,
func
(
t
*
testing
.
T
)
{
testPreimage
(
make
([]
byte
,
1000
))
})
t
.
Run
(
"random"
,
func
(
t
*
testing
.
T
)
{
dat
:=
make
([]
byte
,
1000
)
_
,
_
=
rand
.
Read
(
dat
[
:
])
testPreimage
(
dat
)
})
}
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