Skip to content

Commit b7a089b

Browse files
authored
[entropy] Change provider interface to simplify supporting many chains (#1149)
* grr * cleanup * fix semver
1 parent da72b6e commit b7a089b

File tree

11 files changed

+61
-117
lines changed

11 files changed

+61
-117
lines changed

fortuna/Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

fortuna/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
[package]
22
name = "fortuna"
3-
version = "1.0.0"
3+
version = "2.0.0"
44
edition = "2021"
55

66
[dependencies]
77
anyhow = "1.0.75"
88
axum = { version = "0.6.20", features = ["json", "ws", "macros"] }
99
axum-macros = { version = "0.3.8" }
1010
base64 = { version = "0.21.0" }
11+
bincode = "1.3.3"
1112
byteorder = "1.5.0"
1213
clap = { version = "4.4.6", features = ["derive", "cargo", "env"] }
1314
ethabi = "18.0.0"

fortuna/src/abi.json

Lines changed: 11 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,4 @@
11
[
2-
{
3-
"inputs": [
4-
{
5-
"internalType": "uint256",
6-
"name": "pythFeeInWei",
7-
"type": "uint256"
8-
}
9-
],
10-
"stateMutability": "nonpayable",
11-
"type": "constructor"
12-
},
13-
{
14-
"inputs": [],
15-
"name": "AssertionFailure",
16-
"type": "error"
17-
},
18-
{
19-
"inputs": [],
20-
"name": "IncorrectProviderRevelation",
21-
"type": "error"
22-
},
23-
{
24-
"inputs": [],
25-
"name": "IncorrectUserRevelation",
26-
"type": "error"
27-
},
28-
{
29-
"inputs": [],
30-
"name": "InsufficientFee",
31-
"type": "error"
32-
},
33-
{
34-
"inputs": [],
35-
"name": "NoSuchProvider",
36-
"type": "error"
37-
},
38-
{
39-
"inputs": [],
40-
"name": "OutOfRandomness",
41-
"type": "error"
42-
},
432
{
443
"anonymous": false,
454
"inputs": [
@@ -66,9 +25,9 @@
6625
"type": "uint64"
6726
},
6827
{
69-
"internalType": "bytes32",
28+
"internalType": "bytes",
7029
"name": "commitmentMetadata",
71-
"type": "bytes32"
30+
"type": "bytes"
7231
},
7332
{
7433
"internalType": "uint64",
@@ -92,7 +51,7 @@
9251
}
9352
],
9453
"indexed": false,
95-
"internalType": "struct PythRandomStructs.ProviderInfo",
54+
"internalType": "struct EntropyStructs.ProviderInfo",
9655
"name": "provider",
9756
"type": "tuple"
9857
}
@@ -130,19 +89,14 @@
13089
"name": "providerCommitmentSequenceNumber",
13190
"type": "uint64"
13291
},
133-
{
134-
"internalType": "bytes32",
135-
"name": "providerCommitmentMetadata",
136-
"type": "bytes32"
137-
},
13892
{
13993
"internalType": "uint256",
14094
"name": "blockNumber",
14195
"type": "uint256"
14296
}
14397
],
14498
"indexed": false,
145-
"internalType": "struct PythRandomStructs.Request",
99+
"internalType": "struct EntropyStructs.Request",
146100
"name": "request",
147101
"type": "tuple"
148102
}
@@ -180,19 +134,14 @@
180134
"name": "providerCommitmentSequenceNumber",
181135
"type": "uint64"
182136
},
183-
{
184-
"internalType": "bytes32",
185-
"name": "providerCommitmentMetadata",
186-
"type": "bytes32"
187-
},
188137
{
189138
"internalType": "uint256",
190139
"name": "blockNumber",
191140
"type": "uint256"
192141
}
193142
],
194143
"indexed": false,
195-
"internalType": "struct PythRandomStructs.Request",
144+
"internalType": "struct EntropyStructs.Request",
196145
"name": "request",
197146
"type": "tuple"
198147
},
@@ -337,9 +286,9 @@
337286
"type": "uint64"
338287
},
339288
{
340-
"internalType": "bytes32",
289+
"internalType": "bytes",
341290
"name": "commitmentMetadata",
342-
"type": "bytes32"
291+
"type": "bytes"
343292
},
344293
{
345294
"internalType": "uint64",
@@ -362,7 +311,7 @@
362311
"type": "uint64"
363312
}
364313
],
365-
"internalType": "struct PythRandomStructs.ProviderInfo",
314+
"internalType": "struct EntropyStructs.ProviderInfo",
366315
"name": "info",
367316
"type": "tuple"
368317
}
@@ -412,18 +361,13 @@
412361
"name": "providerCommitmentSequenceNumber",
413362
"type": "uint64"
414363
},
415-
{
416-
"internalType": "bytes32",
417-
"name": "providerCommitmentMetadata",
418-
"type": "bytes32"
419-
},
420364
{
421365
"internalType": "uint256",
422366
"name": "blockNumber",
423367
"type": "uint256"
424368
}
425369
],
426-
"internalType": "struct PythRandomStructs.Request",
370+
"internalType": "struct EntropyStructs.Request",
427371
"name": "req",
428372
"type": "tuple"
429373
}
@@ -444,9 +388,9 @@
444388
"type": "bytes32"
445389
},
446390
{
447-
"internalType": "bytes32",
391+
"internalType": "bytes",
448392
"name": "commitmentMetadata",
449-
"type": "bytes32"
393+
"type": "bytes"
450394
},
451395
{
452396
"internalType": "uint64",

fortuna/src/command/register_provider.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ use {
1111
std::sync::Arc,
1212
};
1313

14+
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
15+
pub struct CommitmentMetadata {
16+
pub seed: [u8; 32],
17+
pub chain_length: u64,
18+
}
19+
1420
/// Register as a randomness provider. This method will generate and commit to a new random
1521
/// hash chain from the configured secret & a newly generated random value.
1622
pub async fn register_provider(opts: &RegisterProviderOptions) -> Result<()> {
@@ -25,21 +31,29 @@ pub async fn register_provider(opts: &RegisterProviderOptions) -> Result<()> {
2531

2632
// Create a new random hash chain.
2733
let random = rand::random::<[u8; 32]>();
28-
let mut chain = PebbleHashChain::from_config(&opts.randomness, &opts.chain_id, random)?;
34+
let commitment_length = opts.randomness.chain_length;
35+
let mut chain = PebbleHashChain::from_config(
36+
&opts.randomness.secret,
37+
&opts.chain_id,
38+
&random,
39+
commitment_length,
40+
)?;
2941

3042
// Arguments to the contract to register our new provider.
3143
let fee_in_wei = opts.fee;
3244
let commitment = chain.reveal()?;
33-
// Store the random seed in the metadata field so that we can regenerate the hash chain
34-
// at-will. (This is secure because you can't generate the chain unless you also have the secret)
35-
let commitment_metadata = random;
36-
let commitment_length = opts.randomness.chain_length;
45+
// Store the random seed and chain length in the metadata field so that we can regenerate the hash
46+
// chain at-will. (This is secure because you can't generate the chain unless you also have the secret)
47+
let commitment_metadata = CommitmentMetadata {
48+
seed: random,
49+
chain_length: commitment_length,
50+
};
3751

3852
if let Some(r) = contract
3953
.register(
4054
fee_in_wei,
4155
commitment,
42-
commitment_metadata,
56+
bincode::serialize(&commitment_metadata)?.into(),
4357
commitment_length,
4458
)
4559
.send()

fortuna/src/command/run.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use {
22
crate::{
33
api,
44
chain::ethereum::PythContract,
5+
command::register_provider::CommitmentMetadata,
56
config::{
67
Config,
78
RunOptions,
@@ -59,8 +60,15 @@ pub async fn run(opts: &RunOptions) -> Result<()> {
5960
// TODO: we may want to load the hash chain in a lazy/fault-tolerant way. If there are many blockchains,
6061
// then it's more likely that some RPC fails. We should tolerate these faults and generate the hash chain
6162
// later when a user request comes in for that chain.
62-
let random: [u8; 32] = provider_info.commitment_metadata;
63-
let hash_chain = PebbleHashChain::from_config(&opts.randomness, &chain_id, random)?;
63+
let metadata =
64+
bincode::deserialize::<CommitmentMetadata>(&provider_info.commitment_metadata)?;
65+
66+
let hash_chain = PebbleHashChain::from_config(
67+
&opts.randomness.secret,
68+
&chain_id,
69+
&metadata.seed,
70+
metadata.chain_length,
71+
)?;
6472
let chain_state = HashChainState {
6573
offsets: vec![provider_info
6674
.original_commitment_sequence_number

fortuna/src/state.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
use {
2-
crate::{
3-
api::ChainId,
4-
config::RandomnessOptions,
5-
},
2+
crate::api::ChainId,
63
anyhow::{
74
ensure,
85
Result,
@@ -36,17 +33,18 @@ impl PebbleHashChain {
3633
}
3734

3835
pub fn from_config(
39-
opts: &RandomnessOptions,
36+
secret: &str,
4037
chain_id: &ChainId,
41-
random: [u8; 32],
38+
random: &[u8; 32],
39+
chain_length: u64,
4240
) -> Result<Self> {
4341
let mut input: Vec<u8> = vec![];
44-
input.extend_from_slice(&hex::decode(opts.secret.clone())?);
42+
input.extend_from_slice(&hex::decode(secret)?);
4543
input.extend_from_slice(&chain_id.as_bytes());
46-
input.extend_from_slice(&random);
44+
input.extend_from_slice(random);
4745

4846
let secret: [u8; 32] = Keccak256::digest(input).into();
49-
Ok(Self::new(secret, opts.chain_length.try_into()?))
47+
Ok(Self::new(secret, chain_length.try_into()?))
5048
}
5149

5250
/// Reveal the next hash in the chain using the previous proof.

target_chains/ethereum/contracts/contracts/entropy/Entropy.sol

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ contract Entropy is IEntropy, EntropyState {
9595
function register(
9696
uint feeInWei,
9797
bytes32 commitment,
98-
bytes32 commitmentMetadata,
98+
bytes calldata commitmentMetadata,
9999
uint64 chainLength
100100
) public override {
101101
if (chainLength == 0) revert EntropyErrors.AssertionFailure();
@@ -186,7 +186,6 @@ contract Entropy is IEntropy, EntropyState {
186186
req.providerCommitment = providerInfo.currentCommitment;
187187
req.providerCommitmentSequenceNumber = providerInfo
188188
.currentCommitmentSequenceNumber;
189-
req.providerCommitmentMetadata = providerInfo.commitmentMetadata;
190189

191190
if (useBlockHash) {
192191
req.blockNumber = block.number;

target_chains/ethereum/contracts/forge-test/Entropy.t.sol

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,14 @@ contract EntropyTest is Test {
4444
random.register(
4545
provider1FeeInWei,
4646
provider1Proofs[0],
47-
bytes32(keccak256(abi.encodePacked(uint256(0x0100)))),
47+
hex"0100",
4848
provider1ChainLength
4949
);
5050

5151
bytes32[] memory hashChain2 = generateHashChain(provider2, 0, 100);
5252
provider2Proofs = hashChain2;
5353
vm.prank(provider2);
54-
random.register(
55-
provider2FeeInWei,
56-
provider2Proofs[0],
57-
bytes32(keccak256(abi.encodePacked(uint256(0x0200)))),
58-
100
59-
);
54+
random.register(provider2FeeInWei, provider2Proofs[0], hex"0200", 100);
6055
}
6156

6257
function generateHashChain(
@@ -323,12 +318,7 @@ contract EntropyTest is Test {
323318
10
324319
);
325320
vm.prank(provider1);
326-
random.register(
327-
provider1FeeInWei,
328-
newHashChain[0],
329-
bytes32(keccak256(abi.encodePacked(uint256(0x0100)))),
330-
10
331-
);
321+
random.register(provider1FeeInWei, newHashChain[0], hex"0100", 10);
332322
assertInvariants();
333323
EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo(
334324
provider1
@@ -397,12 +387,7 @@ contract EntropyTest is Test {
397387

398388
// Check that overflowing the fee arithmetic causes the transaction to revert.
399389
vm.prank(provider1);
400-
random.register(
401-
MAX_UINT256,
402-
provider1Proofs[0],
403-
bytes32(keccak256(abi.encodePacked(uint256(0x0100)))),
404-
100
405-
);
390+
random.register(MAX_UINT256, provider1Proofs[0], hex"0100", 100);
406391
vm.expectRevert();
407392
random.getFee(provider1);
408393
}
@@ -452,12 +437,7 @@ contract EntropyTest is Test {
452437

453438
// Reregistering updates the required fees
454439
vm.prank(provider1);
455-
random.register(
456-
12345,
457-
provider1Proofs[0],
458-
bytes32(keccak256(abi.encodePacked(uint256(0x0100)))),
459-
100
460-
);
440+
random.register(12345, provider1Proofs[0], hex"0100", 100);
461441

462442
assertRequestReverts(pythFeeInWei + 12345 - 1, provider1, 42, false);
463443
requestWithFee(user2, pythFeeInWei + 12345, provider1, 42, false);

0 commit comments

Comments
 (0)