Skip to content

Commit e7c2700

Browse files
authored
Add Wasm-compatible adapters for implementing Signer trait that work in web browser. Add sample integrations. (#4017)
## Motivation We want to be able to sign block proposals using EVM-compatible wallets in the web browser. ## Proposal Add `linera-web/signer` directory with sample implementations of `Signer` trait for in-memory private key (`in_memory.ts`) and MetaMask integration (`metamask.ts`). Pre-existing `Wallet` implementations in `linera-web` was adapted to work with the new abstractions. ## Test Plan CI/Manual testing. ## Release Plan - Nothing to do / These changes follow the usual release cycle.
1 parent 5bff536 commit e7c2700

File tree

20 files changed

+10816
-95
lines changed

20 files changed

+10816
-95
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@
2525
# Helm
2626
kubernetes/linera-validator/working/*
2727
node_modules
28+
**/dist/

linera-base/src/crypto/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ impl AccountSecretKey {
129129
AccountSignature::Secp256k1(signature)
130130
}
131131
AccountSecretKey::EvmSecp256k1(secret) => {
132-
let signature = secp256k1::evm::EvmSignature::new(value, secret);
132+
let signature = secp256k1::evm::EvmSignature::new(CryptoHash::new(value), secret);
133133
AccountSignature::EvmSecp256k1(signature)
134134
}
135135
}

linera-base/src/crypto/secp256k1/evm.rs

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,12 @@ pub struct EvmKeyPair {
6767
#[derive(Eq, PartialEq, Copy, Clone)]
6868
pub struct EvmSignature(pub(crate) Signature);
6969

70-
#[cfg(with_testing)]
7170
impl FromStr for EvmSignature {
7271
type Err = CryptoError;
7372

7473
fn from_str(s: &str) -> Result<Self, Self::Err> {
75-
let bytes = hex::decode(s)?;
74+
// If the string starts with "0x", we remove it before decoding.
75+
let bytes = hex::decode(s.strip_prefix("0x").unwrap_or(s))?;
7676
let sig = Signature::from_erc2098(&bytes);
7777
Ok(EvmSignature(sig))
7878
}
@@ -195,7 +195,9 @@ impl FromStr for EvmPublicKey {
195195
type Err = CryptoError;
196196

197197
fn from_str(s: &str) -> Result<Self, Self::Err> {
198-
hex::decode(s)?.as_slice().try_into()
198+
hex::decode(s.strip_prefix("0x").unwrap_or(s))?
199+
.as_slice()
200+
.try_into()
199201
}
200202
}
201203

@@ -371,13 +373,9 @@ impl EvmSecretKey {
371373
}
372374

373375
impl EvmSignature {
374-
/// Computes a secp256k1 signature for `value` using the given `secret`.
375-
/// It first serializes the `T` type and then creates the `CryptoHash` from the serialized bytes.
376-
pub fn new<'de, T>(value: &T, secret: &EvmSecretKey) -> Self
377-
where
378-
T: BcsSignable<'de>,
379-
{
380-
Self::sign_prehash(secret, CryptoHash::new(value))
376+
/// Computes a secp256k1 signature for `prehash` using the given `secret`.
377+
pub fn new(prehash: CryptoHash, secret: &EvmSecretKey) -> Self {
378+
Self::sign_prehash(secret, prehash)
381379
}
382380

383381
/// Computes a signature from a prehash.
@@ -533,13 +531,33 @@ mod serde_utils {
533531

534532
#[cfg(with_testing)]
535533
mod tests {
534+
#[test]
535+
fn eip191_compatibility() {
536+
use std::str::FromStr;
537+
538+
use crate::crypto::{CryptoHash, EvmSecretKey, EvmSignature};
539+
540+
// Generated in MetaMask.
541+
let secret_key = "f77a21701522a03b01c111ad2d2cdaf2b8403b47507ee0aec3c2e52b765d7a66";
542+
let signer = EvmSecretKey::from_str(secret_key).unwrap();
543+
544+
let crypto_hash = CryptoHash::from_str(
545+
"c520e2b24b05e70c39c36d4aa98e9129ac0079ea002d4c382e6996ea11946d1e",
546+
)
547+
.unwrap();
548+
549+
let signature = EvmSignature::new(crypto_hash, &signer);
550+
let js_signature = EvmSignature::from_str("0xe257048813b851f812ba6e508e972d8bb09504824692b027ca95d31301dbe8c7103a2f35ce9950d031d260f412dcba09c24027288872a67abe261c0a3e55c9121b").unwrap();
551+
assert_eq!(signature, js_signature);
552+
}
553+
536554
#[test]
537555
fn test_signatures() {
538556
use serde::{Deserialize, Serialize};
539557

540558
use crate::crypto::{
541559
secp256k1::evm::{EvmKeyPair, EvmSignature},
542-
BcsSignable, TestString,
560+
BcsSignable, CryptoHash, TestString,
543561
};
544562

545563
#[derive(Debug, Serialize, Deserialize)]
@@ -551,10 +569,11 @@ mod tests {
551569
let keypair2 = EvmKeyPair::generate();
552570

553571
let ts = TestString("hello".into());
572+
let ts_cryptohash = CryptoHash::new(&ts);
554573
let tsx = TestString("hellox".into());
555574
let foo = Foo("hello".into());
556575

557-
let s = EvmSignature::new(&ts, &keypair1.secret_key);
576+
let s = EvmSignature::new(ts_cryptohash, &keypair1.secret_key);
558577
assert!(s.check(&ts, &keypair1.public_key).is_ok());
559578
assert!(s.check(&ts, &keypair2.public_key).is_err());
560579
assert!(s.check(&tsx, &keypair1.public_key).is_err());
@@ -587,10 +606,11 @@ mod tests {
587606
fn test_signature_serialization() {
588607
use crate::crypto::{
589608
secp256k1::evm::{EvmKeyPair, EvmSignature},
590-
TestString,
609+
CryptoHash, TestString,
591610
};
592611
let keypair = EvmKeyPair::generate();
593-
let sig = EvmSignature::new(&TestString("hello".into()), &keypair.secret_key);
612+
let prehash = CryptoHash::new(&TestString("hello".into()));
613+
let sig = EvmSignature::new(prehash, &keypair.secret_key);
594614
let s = serde_json::to_string(&sig).unwrap();
595615
let sig2: EvmSignature = serde_json::from_str(&s).unwrap();
596616
assert_eq!(sig, sig2);
@@ -628,10 +648,11 @@ mod tests {
628648
fn human_readable_ser() {
629649
use crate::crypto::{
630650
secp256k1::evm::{EvmKeyPair, EvmSignature},
631-
TestString,
651+
CryptoHash, TestString,
632652
};
633653
let key_pair = EvmKeyPair::generate();
634-
let sig = EvmSignature::new(&TestString("hello".into()), &key_pair.secret_key);
654+
let prehash = CryptoHash::new(&TestString("hello".into()));
655+
let sig = EvmSignature::new(prehash, &key_pair.secret_key);
635656
let s = serde_json::to_string(&sig).unwrap();
636657
let sig2: EvmSignature = serde_json::from_str(&s).unwrap();
637658
assert_eq!(sig, sig2);

linera-chain/src/data_types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -754,10 +754,10 @@ mod signing {
754754
use std::str::FromStr;
755755

756756
// Generated in MetaMask.
757-
let pk = "f77a21701522a03b01c111ad2d2cdaf2b8403b47507ee0aec3c2e52b765d7a66";
757+
let secret_key = "f77a21701522a03b01c111ad2d2cdaf2b8403b47507ee0aec3c2e52b765d7a66";
758758

759759
let signer: AccountSecretKey = AccountSecretKey::EvmSecp256k1(
760-
linera_base::crypto::EvmSecretKey::from_str(pk).unwrap(),
760+
linera_base::crypto::EvmSecretKey::from_str(secret_key).unwrap(),
761761
);
762762

763763
let proposed_block = ProposedBlock {

linera-execution/src/wasm/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ pub enum WasmExecutionError {
307307
LoadServiceModule(#[source] anyhow::Error),
308308
#[error("Failed to instrument Wasm module to add fuel metering")]
309309
InstrumentModule,
310-
#[error("Invalid Wasm module")]
310+
#[error("Invalid Wasm module: {0}")]
311311
InvalidBytecode(#[from] wasm_instrument::parity_wasm::SerializationError),
312312
#[cfg(with_wasmer)]
313313
#[error("Failed to instantiate Wasm module: {_0}")]

linera-rpc/tests/format.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// SPDX-License-Identifier: Apache-2.0
44

55
use linera_base::{
6-
crypto::{AccountPublicKey, AccountSignature, TestString},
6+
crypto::{AccountPublicKey, AccountSignature, CryptoHash, TestString},
77
data_types::{BlobContent, ChainDescription, ChainOrigin, OracleResponse, Round},
88
identifiers::{AccountOwner, BlobType, GenericApplicationId},
99
ownership::ChainOwnership,
@@ -47,7 +47,7 @@ fn get_registry() -> Result<Registry> {
4747
let evm_public_key = evm_secret_key.public();
4848
tracer.trace_value(&mut samples, &evm_public_key)?;
4949
let evm_signature = linera_base::crypto::EvmSignature::new(
50-
&TestString::new("signature".to_string()),
50+
CryptoHash::new(&TestString::new("signature".to_string())),
5151
&evm_secret_key,
5252
);
5353
tracer.trace_value(&mut samples, &evm_signature)?;

linera-web/package.json

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,33 @@
11
{
2-
"name": "@linera/client",
3-
"version": "0.14.0",
4-
"description": "Web client for the Linera blockchain",
5-
"main": "dist/linera_web.js",
6-
"types": "dist/linera_web.d.ts",
7-
"files": [ "dist" ],
8-
"type": "module",
9-
"scripts": {
10-
"install": "true",
11-
"build": "bash build.bash --release",
12-
"prepare": "pnpm build",
13-
"lint": "cargo fmt --check && cargo clippy -- -W clippy::pedantic",
14-
"ci": "pnpm lint"
15-
},
16-
"keywords": [],
17-
"author": "Linera <[email protected]>",
18-
"license": "Apache-2.0",
19-
"repository": {
20-
"type": "git",
21-
"url": "git+https://github.com/linera-io/linera-web.git"
22-
},
23-
"bugs": {
24-
"url": "https://github.com/linera-io/linera-web/issues"
25-
},
26-
"homepage": "https://github.com/linera-io/linera-web#readme"
2+
"name": "@linera/client",
3+
"version": "0.14.0",
4+
"description": "Web client for the Linera blockchain",
5+
"main": "dist/linera_web.js",
6+
"types": "dist/linera_web.d.ts",
7+
"files": [
8+
"dist"
9+
],
10+
"type": "module",
11+
"scripts": {
12+
"install": "true",
13+
"build": "bash build.bash --release",
14+
"prepare": "pnpm build",
15+
"lint": "cargo fmt --check && cargo clippy -- -W clippy::pedantic",
16+
"ci": "pnpm lint",
17+
"format": "pnpm exec prettier . --write"
18+
},
19+
"keywords": [],
20+
"author": "Linera <[email protected]>",
21+
"license": "Apache-2.0",
22+
"repository": {
23+
"type": "git",
24+
"url": "git+https://github.com/linera-io/linera-web.git"
25+
},
26+
"bugs": {
27+
"url": "https://github.com/linera-io/linera-web/issues"
28+
},
29+
"homepage": "https://github.com/linera-io/linera-web#readme",
30+
"devDependencies": {
31+
"prettier": "3.5.3"
32+
}
2733
}

0 commit comments

Comments
 (0)