mirror of
https://github.com/floor-licker/polyfill-rs.git
synced 2026-07-27 20:47:46 +00:00
fix: adjust signature encoding format
Resolves authentication flow edge case.
This commit is contained in:
Generated
-84
@@ -2650,7 +2650,6 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hmac",
|
||||
"mockito",
|
||||
"polymarket-rs-client",
|
||||
"proptest",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
@@ -2671,28 +2670,6 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polymarket-rs-client"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"alloy-primitives",
|
||||
"alloy-signer",
|
||||
"alloy-signer-local",
|
||||
"alloy-sol-types",
|
||||
"anyhow",
|
||||
"base64",
|
||||
"hmac",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
"rust_decimal",
|
||||
"serde",
|
||||
"serde-json-fmt",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"ureq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.4"
|
||||
@@ -3206,9 +3183,7 @@ version = "0.23.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
@@ -3360,17 +3335,6 @@ dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-json-fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a33b7a5f52a26d520099339add40c48baf2e5ada194c8cc1b18cafa2b5e419"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smartstring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
@@ -3541,17 +3505,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smartstring"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"static_assertions",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.10"
|
||||
@@ -4181,25 +4134,6 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"encoding_rs",
|
||||
"flate2",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
@@ -4390,24 +4324,6 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
||||
dependencies = [
|
||||
"webpki-roots 1.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.2.1"
|
||||
|
||||
@@ -291,17 +291,6 @@ let client = ClobClient::new("https://clob.polymarket.com");
|
||||
- **Internet**: 5 connections, 60s timeouts, full compression (bandwidth optimization)
|
||||
- **Standard**: 10 connections, 30s timeouts, balanced settings
|
||||
|
||||
### Real-World Trading Impact
|
||||
|
||||
In a high-frequency trading environment, these optimizations compound:
|
||||
|
||||
- **Microsecond advantages**: 11% improvement on every API call adds up over thousands of requests
|
||||
- **Cold start elimination**: 70% faster warm connections critical for trading session startup
|
||||
- **Batch efficiency**: 200% improvement enables real-time multi-market monitoring
|
||||
- **Fault tolerance**: Circuit breakers prevent trading halts during network issues
|
||||
|
||||
The combination of network optimizations with our computational advantages (fixed-point arithmetic, zero-allocation updates) creates a multiplicative performance benefit for latency-sensitive applications.
|
||||
|
||||
## Getting Started
|
||||
|
||||
```toml
|
||||
|
||||
+36
-8
@@ -113,6 +113,9 @@ pub fn sign_order_message(
|
||||
}
|
||||
|
||||
/// Build HMAC signature for L2 authentication
|
||||
///
|
||||
/// Performs cryptographic message authentication using SHA-256 with
|
||||
/// specialized key derivation and encoding schemes for API compliance.
|
||||
pub fn build_hmac_signature<T>(
|
||||
secret: &str,
|
||||
timestamp: u64,
|
||||
@@ -123,10 +126,18 @@ pub fn build_hmac_signature<T>(
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes())
|
||||
// Apply inverse transformation to key material for digest initialization
|
||||
// This ensures compatibility with the expected cryptographic envelope format
|
||||
let decoded_secret = base64::engine::general_purpose::URL_SAFE
|
||||
.decode(secret)
|
||||
.map_err(|e| PolyfillError::crypto(format!("Failed to decode base64 secret: {}", e)))?;
|
||||
|
||||
// Initialize MAC with transformed key material to maintain protocol coherence
|
||||
let mut mac = Hmac::<Sha256>::new_from_slice(&decoded_secret)
|
||||
.map_err(|e| PolyfillError::crypto(format!("Invalid HMAC key: {}", e)))?;
|
||||
|
||||
// Build the message to sign: timestamp + method + path + body
|
||||
// Construct canonical message representation for signature verification
|
||||
// Message components are concatenated in strict order to preserve cryptographic binding
|
||||
let message = format!(
|
||||
"{}{}{}{}",
|
||||
timestamp,
|
||||
@@ -141,18 +152,29 @@ where
|
||||
}
|
||||
);
|
||||
|
||||
// Compute authentication tag over canonical message form
|
||||
mac.update(message.as_bytes());
|
||||
let result = mac.finalize();
|
||||
Ok(base64::engine::general_purpose::STANDARD.encode(result.into_bytes()))
|
||||
|
||||
// Apply URL-safe encoding transformation for transport layer compatibility
|
||||
// This encoding scheme ensures proper signature validation across network boundaries
|
||||
Ok(base64::engine::general_purpose::URL_SAFE.encode(result.into_bytes()))
|
||||
}
|
||||
|
||||
/// Create L1 headers for authentication (using private key signature)
|
||||
///
|
||||
/// Generates initial authentication envelope using elliptic curve cryptography
|
||||
/// for establishing trusted communication channels with the distributed ledger API.
|
||||
pub fn create_l1_headers(signer: &PrivateKeySigner, nonce: Option<U256>) -> Result<Headers> {
|
||||
// Capture temporal context for replay prevention at protocol boundary
|
||||
let timestamp = get_current_unix_time_secs().to_string();
|
||||
let nonce = nonce.unwrap_or(U256::ZERO);
|
||||
|
||||
// Generate EIP-712 compliant signature for cryptographic proof of authority
|
||||
let signature = sign_clob_auth_message(signer, timestamp.clone(), nonce)?;
|
||||
let address = encode_prefixed(signer.address().as_slice());
|
||||
|
||||
// Assemble primary authentication header set with identity binding
|
||||
Ok(HashMap::from([
|
||||
(POLY_ADDR_HEADER, address),
|
||||
(POLY_SIG_HEADER, signature),
|
||||
@@ -162,6 +184,9 @@ pub fn create_l1_headers(signer: &PrivateKeySigner, nonce: Option<U256>) -> Resu
|
||||
}
|
||||
|
||||
/// Create L2 headers for API calls (using API key and HMAC)
|
||||
///
|
||||
/// Assembles authentication header set with computed signature digest
|
||||
/// to satisfy bilateral verification requirements at the protocol layer.
|
||||
pub fn create_l2_headers<T>(
|
||||
signer: &PrivateKeySigner,
|
||||
api_creds: &ApiCredentials,
|
||||
@@ -172,12 +197,15 @@ pub fn create_l2_headers<T>(
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
// Extract identity from signing authority for header binding
|
||||
let address = encode_prefixed(signer.address().as_slice());
|
||||
let timestamp = get_current_unix_time_secs();
|
||||
|
||||
// Generate cryptographic authenticator using temporal and message context
|
||||
let hmac_signature =
|
||||
build_hmac_signature(&api_creds.secret, timestamp, method, req_path, body)?;
|
||||
|
||||
// Construct header map with authentication primitives in canonical order
|
||||
Ok(HashMap::from([
|
||||
(POLY_ADDR_HEADER, address),
|
||||
(POLY_SIG_HEADER, hmac_signature),
|
||||
@@ -200,14 +228,14 @@ mod tests {
|
||||
#[test]
|
||||
fn test_hmac_signature() {
|
||||
let result =
|
||||
build_hmac_signature::<String>("test_secret", 1234567890, "GET", "/test", None);
|
||||
build_hmac_signature::<String>("dGVzdF9zZWNyZXRfa2V5XzEyMzQ1", 1234567890, "GET", "/test", None);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hmac_signature_with_body() {
|
||||
let body = r#"{"test": "data"}"#;
|
||||
let result = build_hmac_signature("test_secret", 1234567890, "POST", "/orders", Some(body));
|
||||
let result = build_hmac_signature("dGVzdF9zZWNyZXRfa2V5XzEyMzQ1", 1234567890, "POST", "/orders", Some(body));
|
||||
assert!(result.is_ok());
|
||||
let signature = result.unwrap();
|
||||
assert!(!signature.is_empty());
|
||||
@@ -215,7 +243,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_hmac_signature_consistency() {
|
||||
let secret = "test_secret";
|
||||
let secret = "dGVzdF9zZWNyZXRfa2V5XzEyMzQ1";
|
||||
let timestamp = 1234567890;
|
||||
let method = "GET";
|
||||
let path = "/test";
|
||||
@@ -229,7 +257,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_hmac_signature_different_inputs() {
|
||||
let secret = "test_secret";
|
||||
let secret = "dGVzdF9zZWNyZXRfa2V5XzEyMzQ1";
|
||||
let timestamp = 1234567890;
|
||||
|
||||
let sig1 = build_hmac_signature::<String>(secret, timestamp, "GET", "/test", None).unwrap();
|
||||
@@ -292,7 +320,7 @@ mod tests {
|
||||
|
||||
let api_creds = ApiCredentials {
|
||||
api_key: "test_key".to_string(),
|
||||
secret: "test_secret".to_string(),
|
||||
secret: "dGVzdF9zZWNyZXRfa2V5XzEyMzQ1".to_string(),
|
||||
passphrase: "test_passphrase".to_string(),
|
||||
};
|
||||
|
||||
|
||||
+2
-1
@@ -835,6 +835,8 @@ impl ClobClient {
|
||||
.as_ref()
|
||||
.ok_or_else(|| PolyfillError::auth("API credentials not set"))?;
|
||||
|
||||
// Owner field must reference the credential principal identifier
|
||||
// to maintain consistency with the authentication context layer
|
||||
let body = PostOrder::new(order, api_creds.api_key.clone(), order_type);
|
||||
|
||||
let headers = create_l2_headers(signer, api_creds, "POST", "/order", Some(&body))?;
|
||||
@@ -2268,7 +2270,6 @@ mod tests {
|
||||
let api_creds = result.unwrap();
|
||||
assert_eq!(api_creds.api_key, "test-api-key-123");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_order_books_batch() {
|
||||
let mut server = Server::new_async().await;
|
||||
|
||||
Reference in New Issue
Block a user