problem: users want to verify that response are coming from their nodes
solution: edge node can sign received valued Co-authored-by: Igor Artamonov <igor@artamonov.ru>
This commit is contained in:
committed by
GitHub
parent
530811d272
commit
d1ad77a345
@@ -21,14 +21,19 @@ import io.netty.handler.ssl.ClientAuth
|
||||
import io.netty.handler.ssl.OpenSsl
|
||||
import io.netty.handler.ssl.OpenSslServerContext
|
||||
import io.netty.handler.ssl.SslContext
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import spock.lang.Specification
|
||||
import sun.security.x509.X509CertImpl
|
||||
|
||||
import java.security.Security
|
||||
|
||||
class TlsSetupSpec extends Specification {
|
||||
|
||||
TlsSetup tlsSetup = new TlsSetup(new FileResolver(new File("src/test/resources/tls-local")))
|
||||
|
||||
def setup() {
|
||||
def setupSpec() {
|
||||
Security.addProvider(new BouncyCastleProvider())
|
||||
|
||||
// !!!!!!!!!!!!
|
||||
// run test on OS with OpenSSL installed
|
||||
// !!!!!!!!!!!!
|
||||
@@ -148,12 +153,13 @@ class TlsSetupSpec extends Specification {
|
||||
def config = new AuthConfig.ServerTlsAuth(
|
||||
enabled: true,
|
||||
certificate: "127.0.0.1.crt",
|
||||
key: "127.0.0.1.key",
|
||||
// note that JDK Security doesn't accept non-P8 keys, but with Bouncy Castle we should test with a really invalid key
|
||||
key: "127.0.0.1.invalid.key",
|
||||
)
|
||||
when:
|
||||
tlsSetup.setupServer("test", config, false)
|
||||
then:
|
||||
def t = thrown(IllegalArgumentException)
|
||||
thrown(Exception)
|
||||
}
|
||||
|
||||
def "Fail if client certificate not set but required"() {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.emeraldpay.dshackle.config
|
||||
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import org.bouncycastle.util.io.pem.PemObject
|
||||
import org.bouncycastle.util.io.pem.PemWriter
|
||||
import spock.lang.Specification
|
||||
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.SecureRandom
|
||||
import java.security.spec.ECGenParameterSpec
|
||||
import java.security.spec.PKCS8EncodedKeySpec
|
||||
|
||||
class SignatureConfigReaderSpec extends Specification {
|
||||
|
||||
def "Parse enabled"() {
|
||||
setup:
|
||||
def config = "signed-response:\n" +
|
||||
" enabled: true\n" +
|
||||
" algorithm: SECP256K1\n" +
|
||||
" private-key: /root/key.pem\n"
|
||||
|
||||
when:
|
||||
def reader = new SignatureConfigReader(TestingCommons.fileResolver())
|
||||
def act = reader.read(new ByteArrayInputStream(config.bytes))
|
||||
|
||||
then:
|
||||
act.enabled
|
||||
act.privateKey == "/root/key.pem"
|
||||
act.algorithm == SignatureConfig.Algorithm.SECP256K1
|
||||
}
|
||||
|
||||
def "No path when disabled"() {
|
||||
setup:
|
||||
def config = "signed-response:\n" +
|
||||
" enabled: false\n" +
|
||||
" private-key: /root/key.pem\n"
|
||||
|
||||
when:
|
||||
def reader = new SignatureConfigReader(TestingCommons.fileResolver())
|
||||
def act = reader.read(new ByteArrayInputStream(config.bytes))
|
||||
|
||||
then:
|
||||
!act.enabled
|
||||
act.privateKey == null
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class BaseHandlerSpec extends Specification {
|
||||
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
||||
call.items.add(request)
|
||||
call.ids[0] = 5
|
||||
def response = new NativeCall.CallResult(0, '{"foo": 1}'.bytes, null)
|
||||
def response = new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null)
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM, call, requestHandler, false))
|
||||
.collectList()
|
||||
@@ -85,7 +85,7 @@ class BaseHandlerSpec extends Specification {
|
||||
def call = new ProxyCall(ProxyCall.RpcType.BATCH)
|
||||
call.items.add(request)
|
||||
call.ids[0] = 5
|
||||
def response = new NativeCall.CallResult(0, '{"foo": 1}'.bytes, null)
|
||||
def response = new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null)
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM, call, requestHandler, false))
|
||||
.collectList()
|
||||
@@ -116,8 +116,8 @@ class BaseHandlerSpec extends Specification {
|
||||
call.items.add(request2)
|
||||
call.ids[1] = 6
|
||||
def response = [
|
||||
new NativeCall.CallResult(1, '{"foo": 2}'.bytes, null),
|
||||
new NativeCall.CallResult(0, '{"foo": 1}'.bytes, null)
|
||||
new NativeCall.CallResult(1, null, '{"foo": 2}'.bytes, null, null),
|
||||
new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null)
|
||||
]
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM, call, requestHandler, true))
|
||||
@@ -149,8 +149,8 @@ class BaseHandlerSpec extends Specification {
|
||||
call.items.add(request2)
|
||||
call.ids[1] = 6
|
||||
def response = [
|
||||
new NativeCall.CallResult(1, '{"foo": 2}'.bytes, null),
|
||||
new NativeCall.CallResult(0, '{"foo": 1}'.bytes, null)
|
||||
new NativeCall.CallResult(1, null, '{"foo": 2}'.bytes, null, null),
|
||||
new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null)
|
||||
]
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM, call, requestHandler, true))
|
||||
@@ -189,8 +189,8 @@ class BaseHandlerSpec extends Specification {
|
||||
|
||||
// note there is only 2 responses
|
||||
def response = [
|
||||
new NativeCall.CallResult(1, '{"foo": 2}'.bytes, null),
|
||||
new NativeCall.CallResult(2, '{"foo": 3}'.bytes, null)
|
||||
new NativeCall.CallResult(1, null, '{"foo": 2}'.bytes, null, null),
|
||||
new NativeCall.CallResult(2, null, '{"foo": 3}'.bytes, null, null)
|
||||
]
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM, call, requestHandler, true))
|
||||
|
||||
@@ -43,7 +43,7 @@ class HttpHandlerSpec extends Specification {
|
||||
.setMethod("test_test")
|
||||
.setPayload(ByteString.copyFromUtf8("[]"))
|
||||
.build()
|
||||
def respItem = new NativeCall.CallResult(1, "100".bytes, null)
|
||||
def respItem = new NativeCall.CallResult(1, null, "100".bytes, null, null)
|
||||
def req = BlockchainOuterClass.NativeCallRequest.newBuilder()
|
||||
.setChain(Common.ChainRef.CHAIN_ETHEREUM)
|
||||
.addItems(reqItem)
|
||||
@@ -129,7 +129,7 @@ class HttpHandlerSpec extends Specification {
|
||||
def act = handler.execute(Chain.ETHEREUM, call, new AccessHandlerHttp.NoOpHandler(), false)
|
||||
|
||||
then:
|
||||
1 * nativeCall.nativeCallResult(_) >> Flux.just(new NativeCall.CallResult(1, "".bytes, null))
|
||||
1 * nativeCall.nativeCallResult(_) >> Flux.just(new NativeCall.CallResult(1, null, "".bytes, null, null))
|
||||
StepVerifier.create(act)
|
||||
.expectNext("hello")
|
||||
.expectComplete()
|
||||
|
||||
@@ -84,7 +84,7 @@ class WebsocketHandlerSpec extends Specification {
|
||||
|
||||
def "Respond to a single call"() {
|
||||
setup:
|
||||
def response = new NativeCall.CallResult(0, '{"foo": 1}'.bytes, null)
|
||||
def response = new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null)
|
||||
|
||||
def nativeCall = Mock(NativeCall) {
|
||||
1 * it.nativeCallResult(_) >> Flux.fromIterable([response])
|
||||
|
||||
@@ -85,7 +85,7 @@ class WriteRpcJsonSpec extends Specification {
|
||||
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
||||
call.ids[1] = 105
|
||||
def data = [
|
||||
new NativeCall.CallResult(1, '"0x98dbb1"'.bytes, null)
|
||||
new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null)
|
||||
]
|
||||
when:
|
||||
def act = writer.toJson(call, data[0])
|
||||
@@ -98,7 +98,7 @@ class WriteRpcJsonSpec extends Specification {
|
||||
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
||||
call.ids[1] = 1
|
||||
def data = [
|
||||
new NativeCall.CallResult(1, null, new NativeCall.CallError(1, "Internal Error", null))
|
||||
new NativeCall.CallResult(1, null, null, new NativeCall.CallError(1, "Internal Error", null), null)
|
||||
]
|
||||
when:
|
||||
def act = writer.toJson(call, data[0])
|
||||
@@ -111,7 +111,7 @@ class WriteRpcJsonSpec extends Specification {
|
||||
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
||||
call.ids[1] = "aaa"
|
||||
def data = [
|
||||
new NativeCall.CallResult(1, '"0x98dbb1"'.bytes, null)
|
||||
new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null)
|
||||
]
|
||||
when:
|
||||
def act = writer.toJson(call, data[0])
|
||||
@@ -126,9 +126,9 @@ class WriteRpcJsonSpec extends Specification {
|
||||
call.ids[2] = 11
|
||||
call.ids[3] = 15
|
||||
def data = [
|
||||
new NativeCall.CallResult(1, '"0x98dbb1"'.bytes, null),
|
||||
new NativeCall.CallResult(2, null, new NativeCall.CallError(2, "oops", null)),
|
||||
new NativeCall.CallResult(3, '{"hash": "0x2484f459dc"}'.bytes, null),
|
||||
new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null),
|
||||
new NativeCall.CallResult(2, null, null, new NativeCall.CallError(2, "oops", null), null),
|
||||
new NativeCall.CallResult(3, null, '{"hash": "0x2484f459dc"}'.bytes, null, null),
|
||||
]
|
||||
when:
|
||||
def act = Flux.fromIterable(data)
|
||||
@@ -154,7 +154,7 @@ class WriteRpcJsonSpec extends Specification {
|
||||
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
||||
call.ids[1] = 10
|
||||
def data = [
|
||||
new NativeCall.CallResult(1, '"0x1"'.bytes, null),
|
||||
new NativeCall.CallResult(1, null, '"0x1"'.bytes, null, null),
|
||||
]
|
||||
when:
|
||||
def act = Flux.fromIterable(data)
|
||||
|
||||
@@ -17,6 +17,7 @@ package io.emeraldpay.dshackle.quorum
|
||||
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
|
||||
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||
import spock.lang.Specification
|
||||
|
||||
class AlwaysQuorumSpec extends Specification {
|
||||
@@ -26,7 +27,7 @@ class AlwaysQuorumSpec extends Specification {
|
||||
def quorum = new AlwaysQuorum()
|
||||
def up = Stub(Upstream)
|
||||
when:
|
||||
quorum.record(new JsonRpcException(1, "test"), up)
|
||||
quorum.record(new JsonRpcException(1, "test"), null, up)
|
||||
then:
|
||||
quorum.isFailed()
|
||||
!quorum.isResolved()
|
||||
@@ -41,10 +42,11 @@ class AlwaysQuorumSpec extends Specification {
|
||||
def quorum = new AlwaysQuorum()
|
||||
def up = Stub(Upstream)
|
||||
when:
|
||||
quorum.record("123".bytes, up)
|
||||
quorum.record("123".bytes, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
then:
|
||||
quorum.isResolved()
|
||||
quorum.getResult() == "123".bytes
|
||||
quorum.signature == new ResponseSigner.Signature("sig1".bytes, "test", 100)
|
||||
!quorum.isFailed()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,21 +43,21 @@ class BroadcastQuorumSpec extends Specification {
|
||||
!q.isResolved()
|
||||
|
||||
when:
|
||||
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, upstream1)
|
||||
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null, upstream1)
|
||||
then:
|
||||
!q.isResolved()
|
||||
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _)
|
||||
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _, _)
|
||||
|
||||
when:
|
||||
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, upstream2)
|
||||
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null, upstream2)
|
||||
then:
|
||||
!q.isResolved()
|
||||
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _)
|
||||
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _, _)
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Nonce too low"), upstream3)
|
||||
q.record(new JsonRpcException(1, "Nonce too low"), null, upstream3)
|
||||
then:
|
||||
1 * q.recordError(_, _, _)
|
||||
1 * q.recordError(_, _, _, _)
|
||||
q.isResolved()
|
||||
objectMapper.readValue(q.result, Object) == "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"
|
||||
}
|
||||
@@ -75,21 +75,21 @@ class BroadcastQuorumSpec extends Specification {
|
||||
!q.isResolved()
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Internal error"), upstream1)
|
||||
q.record(new JsonRpcException(1, "Internal error"), null, upstream1)
|
||||
then:
|
||||
!q.isResolved()
|
||||
1 * q.recordError(_, _, _)
|
||||
1 * q.recordError(_, _, _, _)
|
||||
|
||||
when:
|
||||
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, upstream2)
|
||||
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null, upstream2)
|
||||
then:
|
||||
!q.isResolved()
|
||||
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _)
|
||||
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _, _)
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Nonce too low"), upstream3)
|
||||
q.record(new JsonRpcException(1, "Nonce too low"), null, upstream3)
|
||||
then:
|
||||
1 * q.recordError(_, _, _)
|
||||
1 * q.recordError(_, _, _, _)
|
||||
q.isResolved()
|
||||
objectMapper.readValue(q.result, Object) == "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"
|
||||
}
|
||||
@@ -99,19 +99,19 @@ class BroadcastQuorumSpec extends Specification {
|
||||
def quorum = new BroadcastQuorum(3)
|
||||
def up = Stub(Upstream)
|
||||
when:
|
||||
quorum.record(new JsonRpcException(1, "test 1"), up)
|
||||
quorum.record(new JsonRpcException(1, "test 1"), null, up)
|
||||
then:
|
||||
!quorum.isFailed()
|
||||
!quorum.isResolved()
|
||||
|
||||
when:
|
||||
quorum.record(new JsonRpcException(1, "test 2"), up)
|
||||
quorum.record(new JsonRpcException(1, "test 2"), null, up)
|
||||
then:
|
||||
!quorum.isFailed()
|
||||
!quorum.isResolved()
|
||||
|
||||
when:
|
||||
quorum.record(new JsonRpcException(1, "test 3"), up)
|
||||
quorum.record(new JsonRpcException(1, "test 3"), null, up)
|
||||
then:
|
||||
quorum.isFailed()
|
||||
!quorum.isResolved()
|
||||
|
||||
@@ -38,22 +38,23 @@ class NonEmptyQuorumSpec extends Specification {
|
||||
!q.isFailed()
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Internal"), upstream1)
|
||||
q.record(new JsonRpcException(1, "Internal"), null, upstream1)
|
||||
then:
|
||||
!q.isResolved()
|
||||
!q.isFailed()
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Internal"), upstream2)
|
||||
q.record(new JsonRpcException(1, "Internal"), null, upstream2)
|
||||
then:
|
||||
!q.isResolved()
|
||||
!q.isFailed()
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Internal"), upstream3)
|
||||
q.record(new JsonRpcException(1, "Internal"), null, upstream3)
|
||||
then:
|
||||
q.isFailed()
|
||||
!q.isResolved()
|
||||
q.signature == null
|
||||
}
|
||||
|
||||
def "Fail first if not error"() {
|
||||
@@ -70,7 +71,7 @@ class NonEmptyQuorumSpec extends Specification {
|
||||
!q.isFailed()
|
||||
|
||||
when:
|
||||
q.record('"0x11"'.bytes, upstream1)
|
||||
q.record('"0x11"'.bytes, null, upstream1)
|
||||
then:
|
||||
q.isResolved()
|
||||
!q.isFailed()
|
||||
@@ -90,14 +91,14 @@ class NonEmptyQuorumSpec extends Specification {
|
||||
!q.isFailed()
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Internal"), upstream1)
|
||||
q.record(new JsonRpcException(1, "Internal"), null, upstream1)
|
||||
then:
|
||||
!q.isFailed()
|
||||
!q.isResolved()
|
||||
|
||||
q.signature == null
|
||||
|
||||
when:
|
||||
q.record('"0x11"'.bytes, upstream2)
|
||||
q.record('"0x11"'.bytes, null, upstream2)
|
||||
then:
|
||||
q.isResolved()
|
||||
!q.isFailed()
|
||||
@@ -117,14 +118,14 @@ class NonEmptyQuorumSpec extends Specification {
|
||||
!q.isFailed()
|
||||
|
||||
when:
|
||||
q.record('null'.bytes, upstream2)
|
||||
q.record('null'.bytes, null, upstream2)
|
||||
then:
|
||||
!q.isFailed()
|
||||
!q.isResolved()
|
||||
|
||||
|
||||
when:
|
||||
q.record('"0x11"'.bytes, upstream2)
|
||||
q.record('"0x11"'.bytes, null, upstream2)
|
||||
then:
|
||||
q.isResolved()
|
||||
!q.isFailed()
|
||||
|
||||
@@ -43,21 +43,21 @@ class NonceQuorumSpec extends Specification {
|
||||
!q.isResolved()
|
||||
|
||||
when:
|
||||
q.record('"0x10"'.bytes, upstream1)
|
||||
q.record('"0x10"'.bytes, null, upstream1)
|
||||
then:
|
||||
!q.isResolved()
|
||||
1 * q.recordValue(_, "0x10", _)
|
||||
1 * q.recordValue(_, "0x10", _, _)
|
||||
|
||||
when:
|
||||
q.record('"0x11"'.bytes, upstream2)
|
||||
q.record('"0x11"'.bytes, null, upstream2)
|
||||
then:
|
||||
!q.isResolved()
|
||||
1 * q.recordValue(_, "0x11", _)
|
||||
1 * q.recordValue(_, "0x11", _, _)
|
||||
|
||||
when:
|
||||
q.record('"0x10"'.bytes, upstream3)
|
||||
q.record('"0x10"'.bytes, null, upstream3)
|
||||
then:
|
||||
1 * q.recordValue(_, "0x10", _)
|
||||
1 * q.recordValue(_, "0x10", _, _)
|
||||
q.isResolved()
|
||||
objectMapper.readValue(q.result, Object) == "0x11"
|
||||
}
|
||||
@@ -75,27 +75,27 @@ class NonceQuorumSpec extends Specification {
|
||||
!q.isResolved()
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Internal"), upstream1)
|
||||
q.record(new JsonRpcException(1, "Internal"), null, upstream1)
|
||||
then:
|
||||
!q.isResolved()
|
||||
1 * q.recordError(_, _, _)
|
||||
1 * q.recordError(_, _, _, _)
|
||||
|
||||
when:
|
||||
q.record('"0x11"'.bytes, upstream2)
|
||||
q.record('"0x11"'.bytes, null, upstream2)
|
||||
then:
|
||||
!q.isResolved()
|
||||
1 * q.recordValue(_, "0x11", _)
|
||||
1 * q.recordValue(_, "0x11", _, _)
|
||||
|
||||
when:
|
||||
q.record('"0x10"'.bytes, upstream3)
|
||||
q.record('"0x10"'.bytes, null, upstream3)
|
||||
then:
|
||||
1 * q.recordValue(_, "0x10", _)
|
||||
1 * q.recordValue(_, "0x10", _, _)
|
||||
!q.isResolved()
|
||||
|
||||
when:
|
||||
q.record('"0x11"'.bytes, upstream1)
|
||||
q.record('"0x11"'.bytes, null, upstream1)
|
||||
then:
|
||||
1 * q.recordValue(_, "0x11", _)
|
||||
1 * q.recordValue(_, "0x11", _, _)
|
||||
q.isResolved()
|
||||
objectMapper.readValue(q.result, Object) == "0x11"
|
||||
}
|
||||
@@ -114,23 +114,24 @@ class NonceQuorumSpec extends Specification {
|
||||
!q.isFailed()
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Internal"), upstream1)
|
||||
q.record(new JsonRpcException(1, "Internal"), null, upstream1)
|
||||
then:
|
||||
!q.isResolved()
|
||||
!q.isFailed()
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Internal"), upstream2)
|
||||
q.record(new JsonRpcException(1, "Internal"), null, upstream2)
|
||||
then:
|
||||
!q.isResolved()
|
||||
!q.isFailed()
|
||||
|
||||
when:
|
||||
q.record(new JsonRpcException(1, "Internal"), upstream3)
|
||||
q.record(new JsonRpcException(1, "Internal"), null, upstream3)
|
||||
then:
|
||||
q.isFailed()
|
||||
!q.isResolved()
|
||||
q.getError() != null
|
||||
q.getError().message == "Internal"
|
||||
q.signature == null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package io.emeraldpay.dshackle.quorum
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import io.emeraldpay.dshackle.quorum.NotLaggingQuorum
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
|
||||
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||
import io.emeraldpay.etherjar.rpc.RpcException
|
||||
import spock.lang.Specification
|
||||
|
||||
@@ -31,7 +32,7 @@ class NotLaggingQuorumSpec extends Specification {
|
||||
def quorum = new NotLaggingQuorum(1)
|
||||
|
||||
when:
|
||||
quorum.record(value, up)
|
||||
quorum.record(value, null, up)
|
||||
then:
|
||||
1 * up.getLag() >> 0
|
||||
quorum.isResolved()
|
||||
@@ -39,6 +40,22 @@ class NotLaggingQuorumSpec extends Specification {
|
||||
quorum.result == value
|
||||
}
|
||||
|
||||
def "Keeps signature"() {
|
||||
setup:
|
||||
def up = Mock(Upstream)
|
||||
def value = "foo".getBytes()
|
||||
def quorum = new NotLaggingQuorum(1)
|
||||
|
||||
when:
|
||||
quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||
then:
|
||||
1 * up.getLag() >> 0
|
||||
quorum.isResolved()
|
||||
!quorum.isFailed()
|
||||
quorum.result == value
|
||||
quorum.signature == new ResponseSigner.Signature("sig1".bytes, "test", 100)
|
||||
}
|
||||
|
||||
def "Resolves if ok lag"() {
|
||||
setup:
|
||||
def up = Mock(Upstream)
|
||||
@@ -46,7 +63,7 @@ class NotLaggingQuorumSpec extends Specification {
|
||||
def quorum = new NotLaggingQuorum(1)
|
||||
|
||||
when:
|
||||
quorum.record(value, up)
|
||||
quorum.record(value, null, up)
|
||||
then:
|
||||
1 * up.getLag() >> 1
|
||||
quorum.isResolved()
|
||||
@@ -61,7 +78,7 @@ class NotLaggingQuorumSpec extends Specification {
|
||||
def quorum = new NotLaggingQuorum(1)
|
||||
|
||||
when:
|
||||
quorum.record(value, up)
|
||||
quorum.record(value, null, up)
|
||||
then:
|
||||
1 * up.getLag() >> 2
|
||||
!quorum.isResolved()
|
||||
@@ -75,7 +92,7 @@ class NotLaggingQuorumSpec extends Specification {
|
||||
def quorum = new NotLaggingQuorum(1)
|
||||
|
||||
when:
|
||||
quorum.record(new JsonRpcException(-100, "test error"), up)
|
||||
quorum.record(new JsonRpcException(-100, "test error"), null, up)
|
||||
then:
|
||||
1 * up.getLag() >> 1
|
||||
!quorum.isResolved()
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package io.emeraldpay.dshackle.quorum
|
||||
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||
import io.emeraldpay.dshackle.upstream.Head
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import org.jetbrains.annotations.NotNull
|
||||
@@ -66,12 +66,14 @@ class ValueAwareQuorumSpec extends Specification {
|
||||
}
|
||||
|
||||
@Override
|
||||
void recordValue(@NotNull byte[] response, @Nullable Object responseValue, @NotNull Upstream upstream) {
|
||||
void recordValue(@NotNull byte[] response, @Nullable Object responseValue, @Nullable ResponseSigner.Signature signature, @NotNull Upstream upstream) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
void recordError(@Nullable byte[] response, @Nullable String errorMessage, @NotNull Upstream upstream) {
|
||||
void recordError(@Nullable byte[] response, @Nullable String errorMessage, @Nullable ResponseSigner.Signature signature, @NotNull Upstream upstream) {
|
||||
|
||||
}
|
||||
|
||||
@@ -85,6 +87,11 @@ class ValueAwareQuorumSpec extends Specification {
|
||||
return false
|
||||
}
|
||||
|
||||
@Override
|
||||
ResponseSigner.Signature getSignature() {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] getResult() {
|
||||
return new byte[0]
|
||||
|
||||
@@ -31,11 +31,11 @@ import io.emeraldpay.dshackle.upstream.Head
|
||||
import io.emeraldpay.dshackle.upstream.Multistream
|
||||
import io.emeraldpay.dshackle.upstream.Selector
|
||||
import io.emeraldpay.dshackle.upstream.MultistreamHolder
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
||||
import io.emeraldpay.dshackle.upstream.calls.ManagedCallMethods
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||
import io.emeraldpay.grpc.Chain
|
||||
import io.emeraldpay.etherjar.rpc.RpcException
|
||||
import io.emeraldpay.etherjar.rpc.RpcResponseError
|
||||
@@ -51,6 +51,16 @@ class NativeCallSpec extends Specification {
|
||||
|
||||
ObjectMapper objectMapper = Global.objectMapper
|
||||
|
||||
def nativeCall(MultistreamHolder upstreams = null, ResponseSigner signer = null) {
|
||||
if (upstreams == null) {
|
||||
upstreams = Stub(MultistreamHolder)
|
||||
}
|
||||
if (signer == null) {
|
||||
signer = Stub(ResponseSigner)
|
||||
}
|
||||
new NativeCall(upstreams, signer)
|
||||
}
|
||||
|
||||
def "Tries router first"() {
|
||||
def routedApi = Mock(Reader) {
|
||||
1 * read(new JsonRpcRequest("eth_test", [])) >> Mono.just(new JsonRpcResponse("1".bytes, null))
|
||||
@@ -58,11 +68,10 @@ class NativeCallSpec extends Specification {
|
||||
def upstream = Mock(Multistream) {
|
||||
1 * getRoutedApi(_) >> Mono.just(routedApi)
|
||||
}
|
||||
def upstreams = Stub(MultistreamHolder)
|
||||
|
||||
def nativeCall = new NativeCall(upstreams)
|
||||
def nativeCall = nativeCall()
|
||||
def ctx = new NativeCall.ValidCallContext<NativeCall.ParsedCallDetails>(
|
||||
1, upstream, Selector.empty, new AlwaysQuorum(),
|
||||
1, null, upstream, Selector.empty, new AlwaysQuorum(),
|
||||
new NativeCall.ParsedCallDetails("eth_test", [])
|
||||
)
|
||||
|
||||
@@ -72,6 +81,7 @@ class NativeCallSpec extends Specification {
|
||||
act.result == "1".bytes
|
||||
}
|
||||
|
||||
|
||||
def "Return error if router denied the requests"() {
|
||||
def routedApi = Mock(Reader) {
|
||||
1 * read(new JsonRpcRequest("eth_test", [])) >> Mono.error(new RpcException(RpcResponseError.CODE_METHOD_NOT_EXIST, "Test message"))
|
||||
@@ -79,11 +89,10 @@ class NativeCallSpec extends Specification {
|
||||
def upstream = Mock(Multistream) {
|
||||
1 * getRoutedApi(_) >> Mono.just(routedApi)
|
||||
}
|
||||
def upstreams = Stub(MultistreamHolder)
|
||||
|
||||
def nativeCall = new NativeCall(upstreams)
|
||||
def nativeCall = nativeCall()
|
||||
def ctx = new NativeCall.ValidCallContext<NativeCall.ParsedCallDetails>(
|
||||
15, upstream, Selector.empty, new AlwaysQuorum(),
|
||||
15, null, upstream, Selector.empty, new AlwaysQuorum(),
|
||||
new NativeCall.ParsedCallDetails("eth_test", [])
|
||||
)
|
||||
|
||||
@@ -102,13 +111,13 @@ class NativeCallSpec extends Specification {
|
||||
setup:
|
||||
def quorum = new AlwaysQuorum()
|
||||
|
||||
def nativeCall = new NativeCall(Stub(MultistreamHolder))
|
||||
def nativeCall = nativeCall()
|
||||
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||
1 * create(_, _) >> Mock(Reader) {
|
||||
1 * read(_) >> Mono.just(new QuorumRpcReader.Result("\"foo\"".bytes, 1))
|
||||
1 * create(_, _, _) >> Mock(Reader) {
|
||||
1 * read(_) >> Mono.just(new QuorumRpcReader.Result("\"foo\"".bytes, null, 1))
|
||||
}
|
||||
}
|
||||
def call = new NativeCall.ValidCallContext(1, TestingCommons.multistream(TestingCommons.api()), Selector.empty, quorum,
|
||||
def call = new NativeCall.ValidCallContext(1, 10, TestingCommons.multistream(TestingCommons.api()), Selector.empty, quorum,
|
||||
new NativeCall.ParsedCallDetails("eth_test", []))
|
||||
|
||||
when:
|
||||
@@ -116,19 +125,20 @@ class NativeCallSpec extends Specification {
|
||||
def act = objectMapper.readValue(resp.result, Object)
|
||||
then:
|
||||
act == "foo"
|
||||
resp.nonce == 10
|
||||
}
|
||||
|
||||
def "Returns error if no quorum"() {
|
||||
setup:
|
||||
def quorum = new AlwaysQuorum()
|
||||
|
||||
def nativeCall = new NativeCall(Stub(MultistreamHolder))
|
||||
def nativeCall = nativeCall()
|
||||
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||
1 * create(_, _) >> Mock(Reader) {
|
||||
1 * read(_) >> Mono.empty()
|
||||
1 * create(_, _, _) >> Mock(Reader) {
|
||||
1 * read(new JsonRpcRequest("eth_test", [], 10)) >> Mono.empty()
|
||||
}
|
||||
}
|
||||
def call = new NativeCall.ValidCallContext(1, TestingCommons.multistream(TestingCommons.api()), Selector.empty, quorum,
|
||||
def call = new NativeCall.ValidCallContext(1, 10, TestingCommons.multistream(TestingCommons.api()), Selector.empty, quorum,
|
||||
new NativeCall.ParsedCallDetails("eth_test", []))
|
||||
|
||||
when:
|
||||
@@ -136,7 +146,7 @@ class NativeCallSpec extends Specification {
|
||||
then:
|
||||
StepVerifier.create(resp)
|
||||
.expectNextMatches { result ->
|
||||
result.isError()
|
||||
result.isError() && result.nonce == 10
|
||||
}
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
@@ -144,8 +154,7 @@ class NativeCallSpec extends Specification {
|
||||
|
||||
def "Packs call exception into response with id"() {
|
||||
setup:
|
||||
def upstreams = Stub(MultistreamHolder)
|
||||
def nativeCall = new NativeCall(upstreams)
|
||||
def nativeCall = nativeCall()
|
||||
when:
|
||||
def resp = nativeCall.processException(new NativeCall.CallFailure(5, new IllegalArgumentException("test test")))
|
||||
then:
|
||||
@@ -161,8 +170,7 @@ class NativeCallSpec extends Specification {
|
||||
|
||||
def "Packs unknown exception into response"() {
|
||||
setup:
|
||||
def upstreams = Stub(MultistreamHolder)
|
||||
def nativeCall = new NativeCall(upstreams)
|
||||
def nativeCall = nativeCall()
|
||||
when:
|
||||
def resp = nativeCall.processException(new IllegalArgumentException("test test"))
|
||||
then:
|
||||
@@ -177,13 +185,12 @@ class NativeCallSpec extends Specification {
|
||||
|
||||
def "Builds normal response"() {
|
||||
setup:
|
||||
def upstreams = Stub(MultistreamHolder)
|
||||
def nativeCall = new NativeCall(upstreams)
|
||||
def nativeCall = nativeCall()
|
||||
def json = [jsonrpc:"2.0", id:1, result: "foo"]
|
||||
|
||||
when:
|
||||
def resp = nativeCall.buildResponse(
|
||||
new NativeCall.CallResult(1561, objectMapper.writeValueAsBytes(json), null)
|
||||
new NativeCall.CallResult(1561, 10, objectMapper.writeValueAsBytes(json), null, null)
|
||||
)
|
||||
then:
|
||||
resp.id == 1561
|
||||
@@ -191,10 +198,28 @@ class NativeCallSpec extends Specification {
|
||||
objectMapper.readValue(resp.payload.toByteArray(), Map.class) == [jsonrpc:"2.0", id:1, result: "foo"]
|
||||
}
|
||||
|
||||
def "Builds response with signature"() {
|
||||
setup:
|
||||
def nativeCall = nativeCall()
|
||||
def json = [jsonrpc:"2.0", id:1, result: "foo"]
|
||||
|
||||
when:
|
||||
def resp = nativeCall.buildResponse(
|
||||
new NativeCall.CallResult(1561, 10, objectMapper.writeValueAsBytes(json), null, new ResponseSigner.Signature("sig1".bytes, "test", 100))
|
||||
)
|
||||
then:
|
||||
resp.id == 1561
|
||||
resp.succeed
|
||||
resp.signature.nonce == 10
|
||||
resp.signature.signature.toByteArray() == "sig1".bytes
|
||||
resp.signature.keyId == 100
|
||||
resp.signature.upstreamId == "test"
|
||||
objectMapper.readValue(resp.payload.toByteArray(), Map.class) == [jsonrpc:"2.0", id:1, result: "foo"]
|
||||
}
|
||||
|
||||
def "Returns error for invalid chain"() {
|
||||
setup:
|
||||
def upstreams = Stub(MultistreamHolder)
|
||||
def nativeCall = new NativeCall(upstreams)
|
||||
def nativeCall = nativeCall()
|
||||
|
||||
def req = BlockchainOuterClass.NativeCallRequest.newBuilder()
|
||||
.setChainValue(0)
|
||||
@@ -210,7 +235,6 @@ class NativeCallSpec extends Specification {
|
||||
then:
|
||||
StepVerifier.create(resp)
|
||||
.expectErrorMatches({t -> t instanceof NativeCall.CallFailure && t.id == 0})
|
||||
// .expectComplete()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
}
|
||||
|
||||
@@ -219,7 +243,7 @@ class NativeCallSpec extends Specification {
|
||||
def upstreams = Mock(MultistreamHolder) {
|
||||
_ * it.observeChains() >> Flux.empty()
|
||||
}
|
||||
def nativeCall = new NativeCall(upstreams)
|
||||
def nativeCall = nativeCall(upstreams)
|
||||
|
||||
def req = BlockchainOuterClass.NativeCallRequest.newBuilder()
|
||||
.setChainValue(Chain.TESTNET_MORDEN.id)
|
||||
@@ -245,13 +269,14 @@ class NativeCallSpec extends Specification {
|
||||
def upstreams = Mock(MultistreamHolder) {
|
||||
_ * it.observeChains() >> Flux.empty()
|
||||
}
|
||||
def nativeCall = new NativeCall(upstreams)
|
||||
def nativeCall = nativeCall(upstreams)
|
||||
|
||||
def req = BlockchainOuterClass.NativeCallRequest.newBuilder()
|
||||
.setChain(Common.ChainRef.CHAIN_ETHEREUM)
|
||||
.addItems(
|
||||
BlockchainOuterClass.NativeCallItem.newBuilder()
|
||||
.setId(1)
|
||||
.setNonce(10)
|
||||
.setMethod("eth_test")
|
||||
.setPayload(ByteString.copyFromUtf8("[]"))
|
||||
)
|
||||
@@ -263,6 +288,7 @@ class NativeCallSpec extends Specification {
|
||||
act.size() == 1
|
||||
with(act[0]) {
|
||||
id == 1
|
||||
nonce == 10
|
||||
payload.method == "eth_test"
|
||||
payload.params == "[]"
|
||||
}
|
||||
@@ -273,7 +299,7 @@ class NativeCallSpec extends Specification {
|
||||
def upstreams = Mock(MultistreamHolder) {
|
||||
_ * it.observeChains() >> Flux.empty()
|
||||
}
|
||||
def nativeCall = new NativeCall(upstreams)
|
||||
def nativeCall = nativeCall(upstreams)
|
||||
|
||||
def req = BlockchainOuterClass.NativeCallRequest.newBuilder()
|
||||
.setChain(Common.ChainRef.CHAIN_ETHEREUM)
|
||||
@@ -300,7 +326,7 @@ class NativeCallSpec extends Specification {
|
||||
def upstreams = Mock(MultistreamHolder) {
|
||||
_ * it.observeChains() >> Flux.empty()
|
||||
}
|
||||
def nativeCall = new NativeCall(upstreams)
|
||||
def nativeCall = nativeCall(upstreams)
|
||||
|
||||
def item = BlockchainOuterClass.NativeCallItem.newBuilder()
|
||||
.setId(1)
|
||||
@@ -338,7 +364,7 @@ class NativeCallSpec extends Specification {
|
||||
def multistreamHolder = Mock(MultistreamHolder) {
|
||||
_ * it.observeChains() >> Flux.empty()
|
||||
}
|
||||
def nativeCall = new NativeCall(multistreamHolder)
|
||||
def nativeCall = nativeCall(multistreamHolder)
|
||||
|
||||
def req = BlockchainOuterClass.NativeCallRequest.newBuilder()
|
||||
.setChain(Common.ChainRef.CHAIN_ETHEREUM)
|
||||
@@ -365,8 +391,8 @@ class NativeCallSpec extends Specification {
|
||||
|
||||
def "Parse empty params"() {
|
||||
setup:
|
||||
def nativeCall = new NativeCall(Stub(MultistreamHolder))
|
||||
def ctx = new NativeCall.ValidCallContext(1, Stub(Multistream), Selector.empty, new AlwaysQuorum(),
|
||||
def nativeCall = nativeCall()
|
||||
def ctx = new NativeCall.ValidCallContext(1, null, Stub(Multistream), Selector.empty, new AlwaysQuorum(),
|
||||
new NativeCall.RawCallDetails("eth_test", "[]"))
|
||||
when:
|
||||
def act = nativeCall.parseParams(ctx)
|
||||
@@ -378,8 +404,8 @@ class NativeCallSpec extends Specification {
|
||||
|
||||
def "Parse none params"() {
|
||||
setup:
|
||||
def nativeCall = new NativeCall(Stub(MultistreamHolder))
|
||||
def ctx = new NativeCall.ValidCallContext(1, Stub(Multistream), Selector.empty, new AlwaysQuorum(),
|
||||
def nativeCall = nativeCall()
|
||||
def ctx = new NativeCall.ValidCallContext(1, null, Stub(Multistream), Selector.empty, new AlwaysQuorum(),
|
||||
new NativeCall.RawCallDetails("eth_test", ""))
|
||||
when:
|
||||
def act = nativeCall.parseParams(ctx)
|
||||
@@ -391,8 +417,8 @@ class NativeCallSpec extends Specification {
|
||||
|
||||
def "Parse single param"() {
|
||||
setup:
|
||||
def nativeCall = new NativeCall(Stub(MultistreamHolder))
|
||||
def ctx = new NativeCall.ValidCallContext(1, Stub(Multistream), Selector.empty, new AlwaysQuorum(),
|
||||
def nativeCall = nativeCall()
|
||||
def ctx = new NativeCall.ValidCallContext(1, null, Stub(Multistream), Selector.empty, new AlwaysQuorum(),
|
||||
new NativeCall.RawCallDetails("eth_test", "[false]"))
|
||||
when:
|
||||
def act = nativeCall.parseParams(ctx)
|
||||
@@ -404,8 +430,8 @@ class NativeCallSpec extends Specification {
|
||||
|
||||
def "Parse multi param"() {
|
||||
setup:
|
||||
def nativeCall = new NativeCall(Stub(MultistreamHolder))
|
||||
def ctx = new NativeCall.ValidCallContext(1, Stub(Multistream), Selector.empty, new AlwaysQuorum(),
|
||||
def nativeCall = nativeCall()
|
||||
def ctx = new NativeCall.ValidCallContext(1, null, Stub(Multistream), Selector.empty, new AlwaysQuorum(),
|
||||
new NativeCall.RawCallDetails("eth_test", "[false, 123]"))
|
||||
when:
|
||||
def act = nativeCall.parseParams(ctx)
|
||||
@@ -419,12 +445,11 @@ class NativeCallSpec extends Specification {
|
||||
//TODO
|
||||
def "Calls cache before remote"() {
|
||||
setup:
|
||||
def upstreams = Stub(MultistreamHolder)
|
||||
def nativeCall = new NativeCall(upstreams)
|
||||
def nativeCall = nativeCall()
|
||||
def api = TestingCommons.api()
|
||||
def upstream = TestingCommons.multistream(api)
|
||||
|
||||
def ctx = new NativeCall.ValidCallContext<NativeCall.ParsedCallDetails>(10,
|
||||
def ctx = new NativeCall.ValidCallContext<NativeCall.ParsedCallDetails>(10, null,
|
||||
upstream,
|
||||
Selector.empty, new AlwaysQuorum(),
|
||||
new NativeCall.ParsedCallDetails("eth_test", []))
|
||||
@@ -438,11 +463,10 @@ class NativeCallSpec extends Specification {
|
||||
//TODO
|
||||
def "Uses cached value"() {
|
||||
setup:
|
||||
def upstreams = Stub(MultistreamHolder)
|
||||
def nativeCall = new NativeCall(upstreams)
|
||||
def nativeCall = nativeCall()
|
||||
def upstream = TestingCommons.multistream(TestingCommons.api())
|
||||
|
||||
def ctx = new NativeCall.ValidCallContext<NativeCall.ParsedCallDetails>(10,
|
||||
def ctx = new NativeCall.ValidCallContext<NativeCall.ParsedCallDetails>(10, null,
|
||||
upstream,
|
||||
Selector.empty, new AlwaysQuorum(),
|
||||
new NativeCall.ParsedCallDetails("eth_test", []))
|
||||
|
||||
@@ -91,7 +91,6 @@ class TrackEthereumTxSpec extends Specification {
|
||||
.setBlock(
|
||||
Common.BlockInfo.newBuilder()
|
||||
.setHeight(blockJson.number)
|
||||
.setWeight(ByteString.copyFrom(blockJson.totalDifficulty.toByteArray()))
|
||||
.setBlockId(blockJson.hash.toHex().substring(2))
|
||||
.setTimestamp(blockJson.timestamp.toEpochMilli())
|
||||
).build()
|
||||
@@ -280,7 +279,6 @@ class TrackEthereumTxSpec extends Specification {
|
||||
.setBlock(
|
||||
Common.BlockInfo.newBuilder()
|
||||
.setHeight(blocks[2].number)
|
||||
.setWeight(ByteString.copyFrom(blocks[2].totalDifficulty.toByteArray()))
|
||||
.setBlockId(blocks[2].hash.toHex().substring(2))
|
||||
.setTimestamp(blocks[2].timestamp.toEpochMilli())
|
||||
)
|
||||
|
||||
@@ -108,7 +108,7 @@ class ApiReaderMock implements Reader<JsonRpcRequest, JsonRpcResponse> {
|
||||
}
|
||||
error = new JsonRpcError(-32601, "Method ${request.method} with ${request.params} is not mocked")
|
||||
}
|
||||
return new JsonRpcResponse(result, error, JsonRpcResponse.Id.from(request.id))
|
||||
return new JsonRpcResponse(result, error, JsonRpcResponse.Id.from(request.id), null)
|
||||
} as Callable<JsonRpcResponse>
|
||||
return Mono.fromCallable(call)
|
||||
}
|
||||
@@ -323,7 +323,7 @@ class ApiReaderMock implements Reader<JsonRpcRequest, JsonRpcResponse> {
|
||||
}
|
||||
|
||||
@Override
|
||||
def <S> NettyOutbound sendUsing(Callable<? extends S> sourceInput, BiFunction<? super Connection, ? super S, ?> mappedInput, Consumer<? super S> sourceCleanup) {
|
||||
<S> NettyOutbound sendUsing(Callable<? extends S> sourceInput, BiFunction<? super Connection, ? super S, ?> mappedInput, Consumer<? super S> sourceCleanup) {
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ class FilteredApisSpec extends Specification {
|
||||
setup:
|
||||
List<Upstream> standard = (0..1).collect {
|
||||
TestingCommons.upstream(
|
||||
it.toString(),
|
||||
"test_" + it,
|
||||
new EthereumApiStub(it)
|
||||
)
|
||||
}
|
||||
@@ -246,7 +246,7 @@ class FilteredApisSpec extends Specification {
|
||||
setup:
|
||||
List<Upstream> standard = (0..1).collect {
|
||||
TestingCommons.upstream(
|
||||
it.toString(),
|
||||
"test_" + it,
|
||||
new EthereumApiStub(it)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ class MultistreamSpec extends Specification {
|
||||
|
||||
def "Filter Best Status accepts better input"() {
|
||||
setup:
|
||||
def up1 = TestingCommons.upstream("1")
|
||||
def up2 = TestingCommons.upstream("2")
|
||||
def up1 = TestingCommons.upstream("test-1")
|
||||
def up2 = TestingCommons.upstream("test-2")
|
||||
def time0 = Instant.now() - Duration.ofSeconds(60)
|
||||
def filter = new Multistream.FilterBestAvailability()
|
||||
def update0 = new Multistream.UpstreamStatus(
|
||||
@@ -87,8 +87,8 @@ class MultistreamSpec extends Specification {
|
||||
|
||||
def "Filter Best Status declines worse input"() {
|
||||
setup:
|
||||
def up1 = TestingCommons.upstream("1")
|
||||
def up2 = TestingCommons.upstream("2")
|
||||
def up1 = TestingCommons.upstream("test-1")
|
||||
def up2 = TestingCommons.upstream("test-2")
|
||||
def time0 = Instant.now() - Duration.ofSeconds(60)
|
||||
def filter = new Multistream.FilterBestAvailability()
|
||||
def update0 = new Multistream.UpstreamStatus(
|
||||
@@ -106,7 +106,7 @@ class MultistreamSpec extends Specification {
|
||||
|
||||
def "Filter Best Status accepts worse input from same upstream"() {
|
||||
setup:
|
||||
def up = TestingCommons.upstream("1")
|
||||
def up = TestingCommons.upstream("test-1")
|
||||
def time0 = Instant.now() - Duration.ofSeconds(60)
|
||||
def filter = new Multistream.FilterBestAvailability()
|
||||
def update0 = new Multistream.UpstreamStatus(
|
||||
@@ -124,8 +124,8 @@ class MultistreamSpec extends Specification {
|
||||
|
||||
def "Filter Best Status accepts any input if existing is outdated"() {
|
||||
setup:
|
||||
def up1 = TestingCommons.upstream("1")
|
||||
def up2 = TestingCommons.upstream("2")
|
||||
def up1 = TestingCommons.upstream("test-1")
|
||||
def up2 = TestingCommons.upstream("test-2")
|
||||
def time0 = Instant.now() - Duration.ofSeconds(90)
|
||||
def filter = new Multistream.FilterBestAvailability()
|
||||
def update0 = new Multistream.UpstreamStatus(
|
||||
@@ -143,9 +143,9 @@ class MultistreamSpec extends Specification {
|
||||
|
||||
def "Filter Best Status declines same status"() {
|
||||
setup:
|
||||
def up1 = TestingCommons.upstream("1")
|
||||
def up2 = TestingCommons.upstream("2")
|
||||
def up3 = TestingCommons.upstream("3")
|
||||
def up1 = TestingCommons.upstream("test-1")
|
||||
def up2 = TestingCommons.upstream("test-2")
|
||||
def up3 = TestingCommons.upstream("test-3")
|
||||
def time0 = Instant.now() - Duration.ofSeconds(60)
|
||||
def filter = new Multistream.FilterBestAvailability()
|
||||
def update0 = new Multistream.UpstreamStatus(
|
||||
@@ -172,7 +172,7 @@ class MultistreamSpec extends Specification {
|
||||
|
||||
def "Call postprocess after api use"() {
|
||||
setup:
|
||||
def request = new JsonRpcRequest("test_foo", [1], 1)
|
||||
def request = new JsonRpcRequest("test_foo", [1], 1, null)
|
||||
|
||||
def api = TestingCommons.api()
|
||||
api.answer("test_foo", [1], "test")
|
||||
|
||||
@@ -13,7 +13,7 @@ class RequestPostprocessorSpec extends Specification {
|
||||
|
||||
def "Wrappers calls onReceive for a value"() {
|
||||
setup:
|
||||
def request = new JsonRpcRequest("test_foo", [1], 1)
|
||||
def request = new JsonRpcRequest("test_foo", [1], 1, null)
|
||||
def processor = Mock(RequestPostprocessor)
|
||||
def api = TestingCommons.api()
|
||||
api.answer("test_foo", [1], "test")
|
||||
@@ -30,7 +30,7 @@ class RequestPostprocessorSpec extends Specification {
|
||||
|
||||
def "Wrappers doesn't call onReceive for no value"() {
|
||||
setup:
|
||||
def request = new JsonRpcRequest("test_foo", [1], 1)
|
||||
def request = new JsonRpcRequest("test_foo", [1], 1, null)
|
||||
def processor = Mock(RequestPostprocessor)
|
||||
Reader<JsonRpcRequest, JsonRpcResponse> reader = Mock(Reader) {
|
||||
1 * it.read(request) >> Mono.empty()
|
||||
|
||||
@@ -50,10 +50,10 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
up, Caches.default(), new CurrentBlockCache(), calls
|
||||
)
|
||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||
1 * create(_, _) >> Mock(Reader) {
|
||||
1 * create(_, _, _) >> Mock(Reader) {
|
||||
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
|
||||
new QuorumRpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(json), 1
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -81,10 +81,10 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
up, Caches.default(), new CurrentBlockCache(), calls
|
||||
)
|
||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||
1 * create(_, _) >> Mock(Reader) {
|
||||
1 * create(_, _, _) >> Mock(Reader) {
|
||||
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
|
||||
new QuorumRpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(null), 1
|
||||
Global.objectMapper.writeValueAsBytes(null), null, 1
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -116,10 +116,10 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
up, Caches.default(), new CurrentBlockCache(), calls
|
||||
)
|
||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||
1 * create(_, _) >> Mock(Reader) {
|
||||
1 * create(_, _, _) >> Mock(Reader) {
|
||||
1 * read(new JsonRpcRequest("eth_getBlockByNumber", ["0x64", false])) >> Mono.just(
|
||||
new QuorumRpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(json), 1
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -152,10 +152,10 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
up, Caches.default(), new CurrentBlockCache(), calls
|
||||
)
|
||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||
1 * create(_, _) >> Mock(Reader) {
|
||||
1 * create(_, _, _) >> Mock(Reader) {
|
||||
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
|
||||
new QuorumRpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(json), 1
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -183,10 +183,10 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
up, Caches.default(), new CurrentBlockCache(), calls
|
||||
)
|
||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||
1 * create(_, _) >> Mock(Reader) {
|
||||
1 * create(_, _, _) >> Mock(Reader) {
|
||||
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
|
||||
new QuorumRpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(null), 1
|
||||
Global.objectMapper.writeValueAsBytes(null), null, 1
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -214,10 +214,10 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
up, Caches.default(), new CurrentBlockCache(), calls
|
||||
)
|
||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||
1 * create(_, _) >> Mock(Reader) {
|
||||
1 * create(_, _, _) >> Mock(Reader) {
|
||||
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "latest"])) >> Mono.just(
|
||||
new QuorumRpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes("0x100"), 1
|
||||
Global.objectMapper.writeValueAsBytes("0x100"), null, 1
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -246,10 +246,10 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
up, Caches.default(), new CurrentBlockCache(), calls
|
||||
)
|
||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||
1 * create(_, _) >> Mock(Reader) {
|
||||
1 * create(_, _, _) >> Mock(Reader) {
|
||||
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "0xa8c9bb"])) >> Mono.just(
|
||||
new QuorumRpcReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes("0x100"), 1
|
||||
Global.objectMapper.writeValueAsBytes("0x100"), null, 1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -37,6 +37,25 @@ class LocalCallRouterSpec extends Specification {
|
||||
act.resultAsProcessedString == "0x0000000000000000000000000000000000000000"
|
||||
}
|
||||
|
||||
def "Returns empty if nonce set"() {
|
||||
setup:
|
||||
def methods = new DefaultEthereumMethods(Chain.ETHEREUM)
|
||||
def router = new LocalCallRouter(
|
||||
new EthereumReader(
|
||||
TestingCommons.multistream(TestingCommons.api()),
|
||||
Caches.default(),
|
||||
ConstantFactory.constantFactory(new DefaultEthereumMethods(Chain.ETHEREUM))
|
||||
),
|
||||
methods,
|
||||
new EmptyHead()
|
||||
)
|
||||
when:
|
||||
def act = router.read(new JsonRpcRequest("eth_getTransactionByHash", ["test"], 10))
|
||||
.block(Duration.ofSeconds(1))
|
||||
then:
|
||||
act == null
|
||||
}
|
||||
|
||||
def "getBlockByNumber with latest uses latest id"() {
|
||||
setup:
|
||||
def head = Mock(Head) {
|
||||
|
||||
@@ -83,7 +83,7 @@ class WsConnectionSpec extends Specification {
|
||||
|
||||
when:
|
||||
Flux.from(ws.handle(wsApiMock.inbound, wsApiMock.outbound)).subscribe()
|
||||
def act = ws.call(new JsonRpcRequest("eth_getTransactionByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200"], 15))
|
||||
def act = ws.call(new JsonRpcRequest("eth_getTransactionByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200"], 15, null))
|
||||
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
@@ -105,7 +105,7 @@ class WsConnectionSpec extends Specification {
|
||||
|
||||
when:
|
||||
Flux.from(ws.handle(wsApiMock.inbound, wsApiMock.outbound)).subscribe()
|
||||
def act = ws.call(new JsonRpcRequest("eth_getTransactionByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200"], 15))
|
||||
def act = ws.call(new JsonRpcRequest("eth_getTransactionByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200"], 15, null))
|
||||
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
@@ -129,7 +129,7 @@ class WsConnectionSpec extends Specification {
|
||||
|
||||
when:
|
||||
Flux.from(ws.handle(wsApiMock.inbound, wsApiMock.outbound)).subscribe()
|
||||
def act = ws.call(new JsonRpcRequest("eth_getTransactionByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200"], 15))
|
||||
def act = ws.call(new JsonRpcRequest("eth_getTransactionByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200"], 15, null))
|
||||
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
|
||||
@@ -30,7 +30,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
when:
|
||||
def act = resp1.equals(resp2)
|
||||
then:
|
||||
act == true
|
||||
act
|
||||
}
|
||||
|
||||
def "Extract processed string without quoted"() {
|
||||
@@ -49,7 +49,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Fails to extract processed string if not quoted"() {
|
||||
when:
|
||||
def act = new JsonRpcResponse("{\"hello\": 1}".bytes, null).resultAsProcessedString
|
||||
new JsonRpcResponse("{\"hello\": 1}".bytes, null).resultAsProcessedString
|
||||
then:
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
@@ -63,7 +63,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize int id and null result"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse("null".bytes, null, new JsonRpcResponse.NumberId(1))
|
||||
def json = new JsonRpcResponse("null".bytes, null, new JsonRpcResponse.NumberId(1), null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -72,7 +72,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize int id and string result"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse('"Hello World"'.bytes, null, new JsonRpcResponse.NumberId(10))
|
||||
def json = new JsonRpcResponse('"Hello World"'.bytes, null, new JsonRpcResponse.NumberId(10), null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -81,7 +81,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize int id and object result"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse('{"foo": "Hello World", "bar": 1}'.bytes, null, new JsonRpcResponse.NumberId(101))
|
||||
def json = new JsonRpcResponse('{"foo": "Hello World", "bar": 1}'.bytes, null, new JsonRpcResponse.NumberId(101), null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -90,7 +90,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize int id and error"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse(null, new JsonRpcError(-32041, "Oooops"), new JsonRpcResponse.NumberId(101))
|
||||
def json = new JsonRpcResponse(null, new JsonRpcError(-32041, "Oooops"), new JsonRpcResponse.NumberId(101), null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -99,7 +99,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize string id and null result"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse("null".bytes, null, new JsonRpcResponse.StringId("asf01t1gg"))
|
||||
def json = new JsonRpcResponse("null".bytes, null, new JsonRpcResponse.StringId("asf01t1gg"), null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -108,7 +108,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize string id and string result"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse('"Hello World"'.bytes, null, new JsonRpcResponse.StringId("10"))
|
||||
def json = new JsonRpcResponse('"Hello World"'.bytes, null, new JsonRpcResponse.StringId("10"), null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -117,7 +117,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
|
||||
def "Serialize string id and object result"() {
|
||||
setup:
|
||||
def json = new JsonRpcResponse('{"foo": "Hello World", "bar": 1}'.bytes, null, new JsonRpcResponse.StringId("g8gk19g"))
|
||||
def json = new JsonRpcResponse('{"foo": "Hello World", "bar": 1}'.bytes, null, new JsonRpcResponse.StringId("g8gk19g"), null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
@@ -128,7 +128,7 @@ class JsonRpcResponseSpec extends Specification {
|
||||
setup:
|
||||
def json = new JsonRpcResponse(null,
|
||||
new JsonRpcError(-32041, "Oooops"),
|
||||
new JsonRpcResponse.StringId("9kbo29gkaasf"))
|
||||
new JsonRpcResponse.StringId("9kbo29gkaasf"), null)
|
||||
when:
|
||||
def act = objectMapper.writeValueAsString(json)
|
||||
then:
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.emeraldpay.dshackle.upstream.signature
|
||||
|
||||
|
||||
import io.emeraldpay.dshackle.config.SignatureConfig
|
||||
import spock.lang.Specification
|
||||
|
||||
class ResponseSignerFactorySpec extends Specification {
|
||||
|
||||
|
||||
def "No signer if not enabled"() {
|
||||
setup:
|
||||
def conf = new SignatureConfig()
|
||||
when:
|
||||
def signer = new ResponseSignerFactory(conf).getObject()
|
||||
then:
|
||||
signer instanceof NoSigner
|
||||
}
|
||||
|
||||
def "No signer if privkey is not configured"() {
|
||||
setup:
|
||||
def conf = new SignatureConfig()
|
||||
when:
|
||||
def signer = new ResponseSignerFactory(conf).getObject()
|
||||
then:
|
||||
signer instanceof NoSigner
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package io.emeraldpay.dshackle.upstream.signature
|
||||
|
||||
import io.emeraldpay.dshackle.config.SignatureConfig
|
||||
import io.emeraldpay.dshackle.config.SignatureConfigReader
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import org.apache.commons.codec.binary.Hex
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import org.bouncycastle.util.io.pem.PemObject
|
||||
import org.bouncycastle.util.io.pem.PemWriter
|
||||
import spock.lang.Specification
|
||||
|
||||
import java.security.KeyFactory
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.MessageDigest
|
||||
import java.security.SecureRandom
|
||||
import java.security.Security
|
||||
import java.security.Signature
|
||||
import java.security.interfaces.ECPrivateKey
|
||||
import java.security.spec.ECGenParameterSpec
|
||||
import java.security.spec.PKCS8EncodedKeySpec
|
||||
|
||||
class Secp256KSignerSpec extends Specification {
|
||||
|
||||
def setupSpec() {
|
||||
Security.addProvider(new BouncyCastleProvider())
|
||||
}
|
||||
|
||||
def "Reads private key"() {
|
||||
setup:
|
||||
def file = File.createTempFile("test", ".pem")
|
||||
def keygen = KeyPairGenerator.getInstance("EC")
|
||||
keygen.initialize(new ECGenParameterSpec("secp256k1"))
|
||||
def key = keygen.generateKeyPair()
|
||||
def keyBuilder = new PKCS8EncodedKeySpec(key.getPrivate().getEncoded())
|
||||
def writer = new PemWriter(new FileWriter(file.path))
|
||||
writer.writeObject(new PemObject("PRIVATE KEY", keyBuilder.getEncoded()))
|
||||
writer.close()
|
||||
|
||||
when:
|
||||
def signer = new ResponseSignerFactory(new SignatureConfig())
|
||||
def act = signer.readKey(SignatureConfig.Algorithm.SECP256K1, file.absolutePath).first
|
||||
|
||||
then:
|
||||
act == key.getPrivate()
|
||||
|
||||
cleanup:
|
||||
file.delete()
|
||||
}
|
||||
|
||||
def "Id is a hash of x509 public key"() {
|
||||
setup:
|
||||
def conf = new SignatureConfig()
|
||||
conf.enabled = true
|
||||
conf.privateKey = "testing/dshackle/test_key"
|
||||
def signer = new ResponseSignerFactory(conf).getObject() as Secp256KSigner
|
||||
|
||||
// To verify the test, check the hash of test key above:
|
||||
//
|
||||
// echo MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE3zetdMdyTO/sTFCLeOrI5moiZt2RjfUVdavhorgqd+gxAqM01cf5Q4QZ8INne9RykcQsbLYXQfDXJbGMm5+gdg== | base64 -d - | shasum -a 256
|
||||
// d25f1ff2c1a57235a9bc7725cd645ab0e9631475a12402f2881579d3f6887597 -
|
||||
//
|
||||
|
||||
when:
|
||||
def id = signer.keyId
|
||||
|
||||
then:
|
||||
id == 0xd25f1ff2c1a57235L
|
||||
}
|
||||
|
||||
def "Wrap message"() {
|
||||
setup:
|
||||
def up = Mock(Upstream) {
|
||||
_ * getId() >> "infura"
|
||||
}
|
||||
def signer = new Secp256KSigner(Stub(ECPrivateKey), 100L)
|
||||
|
||||
when:
|
||||
def act = signer.wrapMessage(10, "test".bytes, up)
|
||||
|
||||
then:
|
||||
act == "DSHACKLESIG/10/infura/9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
|
||||
}
|
||||
|
||||
def "Signed message is valid"() {
|
||||
setup:
|
||||
def result = "test".bytes
|
||||
def up = Mock(Upstream) {
|
||||
_ * getId() >> "infura"
|
||||
}
|
||||
|
||||
def keyPairGen = KeyPairGenerator.getInstance("EC")
|
||||
keyPairGen.initialize(new ECGenParameterSpec("secp256k1"))
|
||||
def pair = keyPairGen.generateKeyPair()
|
||||
def verifier = Signature.getInstance("SHA256withECDSA")
|
||||
verifier.initVerify(pair.getPublic())
|
||||
verifier.update("DSHACKLESIG/10/infura/9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08".getBytes())
|
||||
|
||||
def signer = new Secp256KSigner((pair.getPrivate() as ECPrivateKey), 100L)
|
||||
|
||||
when:
|
||||
def sig = signer.sign(10, result, up)
|
||||
|
||||
then:
|
||||
verifier.verify(sig.value)
|
||||
}
|
||||
|
||||
def "Signed message is valid - for docs"() {
|
||||
// it's the example used in docs
|
||||
setup:
|
||||
def result = '["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", true]'.bytes
|
||||
def up = Mock(Upstream) {
|
||||
_ * getId() >> "infura"
|
||||
}
|
||||
|
||||
def sha256 = MessageDigest.getInstance("SHA-256")
|
||||
|
||||
def conf = new SignatureConfig()
|
||||
conf.enabled = true
|
||||
conf.privateKey = "testing/dshackle/test_key"
|
||||
def factory = new ResponseSignerFactory(conf)
|
||||
|
||||
def sk = factory.readKey(conf.algorithm, conf.privateKey).first
|
||||
def pk = factory.extractPublicKey(KeyFactory.getInstance("EC"), sk)
|
||||
def verifier = Signature.getInstance("SHA256withECDSA")
|
||||
verifier.initVerify(pk)
|
||||
verifier.update("DSHACKLESIG/10/infura/${Hex.encodeHexString(sha256.digest(result))}".getBytes())
|
||||
|
||||
def signer = factory.getObject() as Secp256KSigner
|
||||
|
||||
when:
|
||||
def sig = signer.sign(10, result, up)
|
||||
println("Signature: ${Hex.encodeHexString(sig.value)}")
|
||||
|
||||
then:
|
||||
verifier.verify(sig.value)
|
||||
}
|
||||
}
|
||||
27
src/test/resources/tls-local/127.0.0.1.invalid.key
Normal file
27
src/test/resources/tls-local/127.0.0.1.invalid.key
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA0x1gOF8K9SFwQ9wohBe/EWsjLYdQ4z2yimW604BS/p1tM6xN
|
||||
4JylRWC3rawllw+cTYjLWdgd5WX43u+6i9TG3Ni19bisZamuO2HitVxEeRY/DPlB
|
||||
zIfobDGxSL/R0S1ug7HKqp/dbkL5XT4AlgZn9sj3ikYU4wLj7YdOQXyAIM5FYq5I
|
||||
brtKqIP0cvQsOsR2mr4gk569bu386rP4NZw75UCLVPrbG6ngkv5YkjVQa0M94RPR
|
||||
JGreRdvyfi17nxjo1ePKh1iWif4ERJjjYk/DhTDquNyYMdBWmGDkO2Xfjc5x830I
|
||||
16G1dq+6zNB1FT25jVFR1Wy+DjjGFHrSG4UnzQIDAQABAoIBAQCX1uj9ol4fMI2u
|
||||
QQpi9zFVNdl3RXvH9PgU0lYtCH6o4lFIeQUKJ6A25fk10Dq5C2E/4sNfOzFFbLIy
|
||||
pfll2QOuk69LrCdSd1f5Hc4Q4uvcq0Nt8ViB4r4oExWPXWdrK2HxFk7NqW15gHIZ
|
||||
vh5tyO29cY2Yxg7/t3R3wnlmYEVHUcS7HmhzgDveNzA0VLza3765ntgwXypY8N2j
|
||||
heEQC1h5kMCurcKJyRXmlsXPRWizX0UBWDrMHFeqyhrH0BlRSFTNC3sKmyYaJQmp
|
||||
daPNRr4zO0yfm8utVSbNHX2OM5DpIO1Ecq9Sd43QI+ATAtxFrhPoYK1rwll267CV
|
||||
cJCRbz+BAoGBAPzz/1Xq8s1lGZ6eWz7H1JlzxYLH+TzQfrV1ym7xJgR/b3rVXiJ+
|
||||
D+qL8zUJDa5xZyflXB7zCg4I7ALmNwMJzVLdIOpEntHK2NtRJ2rp4qH+kMXojays
|
||||
zOGYfbRLNVe+mgAK9Pu8eOi8NzXqkB/S8rml3xqSOpUFsvlc2qiYhGdXAoGBANWo
|
||||
XcQDpisRFcrn3J0+pKU57ZIRjxyOTDlEwH7k+x+PprCRFki80kW22u4l22FdDaip
|
||||
s4vCuAm5tmEogEjINU6ZhSKHxonjaGXfzuZ3gAMk/PN7zFazlgfYEKng+fa1YuZ+
|
||||
3Ubzq6py8enoffJ/PSF/lClKlV5sxjyilxeZmOd7AoGBAMtaJHUf0l4I3tXDnLsV
|
||||
4JKzLpjm3X7uwfNehH/Q0t9EVYKk9/BPYs4h1zywEYwesJNEO7p8w/7mAMSZc5AB
|
||||
zvYmOixvMxEO1C5xKXKS7utCv45SJcE48vat17FVO+h3RmSuYKaI4BZ0Wbfi92q7
|
||||
+BwDGx6zW+EdmcoaOba8FgU1AoGAV2WftWaoukUq3O0rWUceolenznBQUiYDGAn/
|
||||
k+imsKpaTS+MJgTXHp1FwNTLgHBH/g4s26azEYdeCzA+CYecBqLVyuIvXIghVErQ
|
||||
n4WSX7bpoc+qLm0Xme3QIy1cEobwBckvSq6yMe8C9eOcYW2a2/EL8jgIEa/9ByCb
|
||||
HZQ+77ECgYEAxR1eoxc/XV9rcftdaRl7+Db9Qvnhfwu7MFQ3lgomol1N1ckSjwnO
|
||||
wo/HX4+8cMS5QN11d8l2hf+7TyuRCrQBLrYYkrVWb4Z+ote3ejrAsDg90xjOFYWf
|
||||
MWSy7kPeDl3JTEdNPFIIa28EQhZYupD0ihBoVspz2eQ1Y66BOfqC9nU=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
Reference in New Issue
Block a user