Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions Explorer/Assets/DCL/Tests/Editor/DecentralandIdentityShould.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using DCL.Web3;
using DCL.Web3.Abstract;
using DCL.Web3.Chains;
using DCL.Web3.Identities;
using NSubstitute;
using NUnit.Framework;
using System;

namespace DCL.Tests.Editor
{
public class DecentralandIdentityShould
{
private const string ADDRESS = "0x0000000000000000000000000000000000000001";

private static DecentralandIdentity NewIdentity()
{
AuthChain authChain = AuthChain.Create();
authChain.SetSigner(ADDRESS);

authChain.Set(new AuthLink
{
type = AuthLinkType.ECDSA_EPHEMERAL,
payload = "ephemeral payload",
signature = "0xephemeralsignature",
});

IWeb3Account ephemeralAccount = Substitute.For<IWeb3Account>();
ephemeralAccount.Sign(Arg.Any<string>()).Returns("0xentitysignature");

return new DecentralandIdentity(new Web3Address(ADDRESS), ephemeralAccount, DateTime.UtcNow.AddDays(1), authChain, IWeb3Identity.Web3IdentitySource.None);
}

[Test]
public void SignEntityWhileAlive()
{
// Arrange
DecentralandIdentity identity = NewIdentity();

// Act
AuthChain signed = identity.Sign("entityId");

// Assert
Assert.IsTrue(signed.TryGet(AuthLinkType.ECDSA_SIGNED_ENTITY, out AuthLink link));
Assert.AreEqual("entityId", link.payload);

signed.Dispose();
identity.Dispose();
}

[Test]
public void ThrowObjectDisposedOnSignAfterDispose()
{
// Arrange
DecentralandIdentity identity = NewIdentity();

// Act
identity.Dispose();

// Assert
Assert.Throws<ObjectDisposedException>(() => identity.Sign("entityId"));
}

[Test]
public void ThrowObjectDisposedEvenAfterPoolReissuesItsAuthChain()
{
// Arrange
DecentralandIdentity identity = NewIdentity();
identity.Dispose();

// Act - the pool can re-issue the identity's released AuthChain instance with the
// disposed flag reset; the identity guard must not depend on that pooled state.
AuthChain reissued = AuthChain.Create();

// Assert
Assert.Throws<ObjectDisposedException>(() => identity.Sign("entityId"));

reissued.Dispose();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions Explorer/Assets/DCL/Web3/Chains/AuthChain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,16 @@ public class AuthChain : IEnumerable<AuthLink>, IDisposable

private bool disposed;

public static AuthChain Create() =>
POOL.Get()!;
public static AuthChain Create()
{
// Reset the recycled instance: a pooled AuthChain comes back with disposed == true
// (set by the Dispose that released it), which made its next Dispose a no-op — the
// instance never returned to the pool again and reads saw stale state.
AuthChain instance = POOL.Get()!;
instance.disposed = false;
instance.chain.Clear();
return instance;
}

private AuthChain() { }

Expand Down
13 changes: 13 additions & 0 deletions Explorer/Assets/DCL/Web3/Identities/DecentralandIdentity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace DCL.Web3.Identities
{
public class DecentralandIdentity : IWeb3Identity
{
private bool disposed;

public Web3Address Address { get; }
public DateTime Expiration { get; }
public IWeb3Account EphemeralAccount { get; }
Expand All @@ -33,11 +35,22 @@ public DecentralandIdentity(

public void Dispose()
{
if (disposed)
return;

disposed = true;
AuthChain.Dispose();
}

public AuthChain Sign(string entityId)
{
// Tracked on the identity itself: the pooled AuthChain's disposed flag is reset when the
// pool re-issues the instance, so it cannot be trusted after this identity released it.
// ObjectDisposedException (not OperationCanceledException) so the failure is not silently
// swallowed by the standard catch (OperationCanceledException) { } blocks.
if (disposed)
throw new ObjectDisposedException(nameof(DecentralandIdentity), $"Cannot sign, the identity for {Address} has been disposed");

if (Expiration < DateTime.UtcNow)
throw new Web3IdentityException(this, $"Cannot sign, identity has expired: {Expiration:s}");

Expand Down
2 changes: 2 additions & 0 deletions Explorer/Assets/DCL/Web3/Identities/IWeb3IdentityCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ class Fake : IWeb3IdentityCache
{
private readonly IWeb3Identity? identity;

#pragma warning disable CS0067
public event Action? OnIdentityCleared;
public event Action? OnIdentityChanged;
#pragma warning restore CS0067

public Fake() : this(new IWeb3Identity.Random()) { }

Expand Down
Loading